diff options
-rw-r--r-- | cmds/installd/dexopt.cpp | 25 | ||||
-rw-r--r-- | include/audiomanager/IPlayer.h | 4 | ||||
-rw-r--r-- | include/gui/FrameTimestamps.h | 27 | ||||
-rw-r--r-- | include/gui/Surface.h | 11 | ||||
-rw-r--r-- | libs/gui/FrameTimestamps.cpp | 48 | ||||
-rw-r--r-- | libs/gui/Surface.cpp | 81 | ||||
-rw-r--r-- | libs/gui/tests/Surface_test.cpp | 307 | ||||
-rw-r--r-- | opengl/include/EGL/eglext.h | 31 | ||||
-rw-r--r-- | opengl/libs/EGL/eglApi.cpp | 112 | ||||
-rw-r--r-- | opengl/specs/EGL_ANDROID_get_frame_timestamps.txt | 104 | ||||
-rw-r--r-- | opengl/specs/README | 25 | ||||
-rw-r--r-- | services/audiomanager/IPlayer.cpp | 53 | ||||
-rw-r--r-- | services/surfaceflinger/Layer.cpp | 12 | ||||
-rw-r--r-- | services/surfaceflinger/Layer.h | 6 | ||||
-rw-r--r-- | services/surfaceflinger/SurfaceFlinger.cpp | 88 | ||||
-rw-r--r-- | services/surfaceflinger/SurfaceFlinger.h | 29 | ||||
-rw-r--r-- | services/surfaceflinger/SurfaceFlinger_hwc1.cpp | 75 | ||||
-rw-r--r-- | services/vr/vr_window_manager/hwc_callback.cpp | 11 | ||||
-rw-r--r-- | vulkan/libvulkan/driver.cpp | 2 |
19 files changed, 908 insertions, 143 deletions
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp index aa32d6b1e7..0fb207bb44 100644 --- a/cmds/installd/dexopt.cpp +++ b/cmds/installd/dexopt.cpp @@ -854,13 +854,16 @@ static int open_output_file(const char* file_name, bool recreate, int permission return open(file_name, flags, permissions); } -static bool set_permissions_and_ownership(int fd, bool is_public, int uid, const char* path) { +static bool set_permissions_and_ownership( + int fd, bool is_public, int uid, const char* path, bool is_secondary_dex) { + // Primary apks are owned by the system. Secondary dex files are owned by the app. + int owning_uid = is_secondary_dex ? uid : AID_SYSTEM; if (fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP | (is_public ? S_IROTH : 0)) < 0) { ALOGE("installd cannot chmod '%s' during dexopt\n", path); return false; - } else if (fchown(fd, AID_SYSTEM, uid) < 0) { + } else if (fchown(fd, owning_uid, uid) < 0) { ALOGE("installd cannot chown '%s' during dexopt\n", path); return false; } @@ -1009,10 +1012,11 @@ class Dex2oatFileWrapper { // (re)Creates the app image if needed. Dex2oatFileWrapper maybe_open_app_image(const char* out_oat_path, bool profile_guided, - bool is_public, int uid) { + bool is_public, int uid, bool is_secondary_dex) { // Use app images only if it is enabled (by a set image format) and we are compiling // profile-guided (so the app image doesn't conservatively contain all classes). - if (!profile_guided) { + // Note that we don't create an image for secondary dex files. + if (is_secondary_dex || !profile_guided) { return Dex2oatFileWrapper(); } @@ -1043,7 +1047,7 @@ Dex2oatFileWrapper maybe_open_app_image(const char* out_oat_path, bool profile_g } } } else if (!set_permissions_and_ownership( - wrapper_fd.get(), is_public, uid, image_path.c_str())) { + wrapper_fd.get(), is_public, uid, image_path.c_str(), is_secondary_dex)) { ALOGE("installd cannot set owner '%s' for image during dexopt\n", image_path.c_str()); wrapper_fd.reset(-1); } @@ -1101,7 +1105,7 @@ Dex2oatFileWrapper maybe_open_reference_profile(const char* pkgname, bool profil // Opens the vdex files and assigns the input fd to in_vdex_wrapper_fd and the output fd to // out_vdex_wrapper_fd. Returns true for success or false in case of errors. bool open_vdex_files(const char* apk_path, const char* out_oat_path, int dexopt_needed, - const char* instruction_set, bool is_public, int uid, + const char* instruction_set, bool is_public, int uid, bool is_secondary_dex, Dex2oatFileWrapper* in_vdex_wrapper_fd, Dex2oatFileWrapper* out_vdex_wrapper_fd) { CHECK(in_vdex_wrapper_fd != nullptr); @@ -1164,7 +1168,7 @@ bool open_vdex_files(const char* apk_path, const char* out_oat_path, int dexopt_ } } if (!set_permissions_and_ownership(out_vdex_wrapper_fd->get(), is_public, uid, - out_vdex_path_str.c_str())) { + out_vdex_path_str.c_str(), is_secondary_dex)) { ALOGE("installd cannot set owner '%s' for vdex during dexopt\n", out_vdex_path_str.c_str()); return false; } @@ -1187,7 +1191,8 @@ Dex2oatFileWrapper open_oat_out_file(const char* apk_path, const char* oat_dir, [out_oat_path_str]() { unlink(out_oat_path_str.c_str()); }); if (wrapper_fd.get() < 0) { PLOG(ERROR) << "installd cannot open output during dexopt" << out_oat_path; - } else if (!set_permissions_and_ownership(wrapper_fd.get(), is_public, uid, out_oat_path)) { + } else if (!set_permissions_and_ownership( + wrapper_fd.get(), is_public, uid, out_oat_path, is_secondary_dex)) { ALOGE("installd cannot set owner '%s' for output during dexopt\n", out_oat_path); wrapper_fd.reset(-1); } @@ -1445,7 +1450,7 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins Dex2oatFileWrapper in_vdex_fd; Dex2oatFileWrapper out_vdex_fd; if (!open_vdex_files(dex_path, out_oat_path, dexopt_needed, instruction_set, is_public, uid, - &in_vdex_fd, &out_vdex_fd)) { + is_secondary_dex, &in_vdex_fd, &out_vdex_fd)) { return -1; } @@ -1454,7 +1459,7 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins // Create the app image file if needed. Dex2oatFileWrapper image_fd = - maybe_open_app_image(out_oat_path, profile_guided, is_public, uid); + maybe_open_app_image(out_oat_path, profile_guided, is_public, uid, is_secondary_dex); // Open the reference profile if needed. Dex2oatFileWrapper reference_profile_fd = diff --git a/include/audiomanager/IPlayer.h b/include/audiomanager/IPlayer.h index 94afae545d..de5c1c7b64 100644 --- a/include/audiomanager/IPlayer.h +++ b/include/audiomanager/IPlayer.h @@ -20,6 +20,7 @@ #include <stdint.h> #include <sys/types.h> +#include <media/VolumeShaper.h> #include <utils/RefBase.h> #include <utils/Errors.h> #include <binder/IInterface.h> @@ -45,6 +46,9 @@ public: virtual void setStartDelayMs(int delayMs) = 0; + virtual void applyVolumeShaper( + const sp<VolumeShaper::Configuration>& configuration, + const sp<VolumeShaper::Operation>& operation) = 0; }; // ---------------------------------------------------------------------------- diff --git a/include/gui/FrameTimestamps.h b/include/gui/FrameTimestamps.h index 46ca2c2ec0..9e1ae9409e 100644 --- a/include/gui/FrameTimestamps.h +++ b/include/gui/FrameTimestamps.h @@ -95,6 +95,11 @@ struct FrameEvents { std::shared_ptr<FenceTime> releaseFence{FenceTime::NO_FENCE}; }; +struct CompositorTiming { + nsecs_t deadline{0}; + nsecs_t interval{16666667}; + nsecs_t presentLatency{0}; +}; // A short history of frames that are synchronized between the consumer and // producer via deltas. @@ -111,6 +116,8 @@ public: protected: std::array<FrameEvents, MAX_FRAME_HISTORY> mFrames; + + CompositorTiming mCompositorTiming; }; @@ -119,6 +126,16 @@ class ProducerFrameEventHistory : public FrameEventHistory { public: ~ProducerFrameEventHistory() override; + // Public for testing. + static nsecs_t snapToNextTick( + nsecs_t timestamp, nsecs_t tickPhase, nsecs_t tickInterval); + + nsecs_t getNextCompositeDeadline(const nsecs_t now) const; + nsecs_t getCompositeInterval() const { return mCompositorTiming.interval; } + nsecs_t getCompositeToPresentLatency() const { + return mCompositorTiming.presentLatency; + } + // virtual for testing. virtual void updateAcquireFence( uint64_t frameNumber, std::shared_ptr<FenceTime>&& acquire); @@ -189,12 +206,15 @@ class ConsumerFrameEventHistory : public FrameEventHistory { public: ~ConsumerFrameEventHistory() override; + void initializeCompositorTiming(const CompositorTiming& compositorTiming); + void addQueue(const NewFrameEventsEntry& newEntry); void addLatch(uint64_t frameNumber, nsecs_t latchTime); void addPreComposition(uint64_t frameNumber, nsecs_t refreshStartTime); void addPostComposition(uint64_t frameNumber, const std::shared_ptr<FenceTime>& gpuCompositionDone, - const std::shared_ptr<FenceTime>& displayPresent); + const std::shared_ptr<FenceTime>& displayPresent, + const CompositorTiming& compositorTiming); void addRetire(uint64_t frameNumber, const std::shared_ptr<FenceTime>& displayRetire); void addRelease(uint64_t frameNumber, nsecs_t dequeueReadyTime, @@ -244,7 +264,7 @@ public: size_t& count); private: - static size_t minFlattenedSize(); + static constexpr size_t minFlattenedSize(); size_t mIndex{0}; uint64_t mFrameNumber{0}; @@ -306,9 +326,10 @@ public: size_t& count); private: - static size_t minFlattenedSize(); + static constexpr size_t minFlattenedSize(); std::vector<FrameEventsDelta> mDeltas; + CompositorTiming mCompositorTiming; }; diff --git a/include/gui/Surface.h b/include/gui/Surface.h index 966e70dd3b..a3c2bfa25b 100644 --- a/include/gui/Surface.h +++ b/include/gui/Surface.h @@ -135,12 +135,18 @@ public: status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence, float outTransformMatrix[16]); + status_t getDisplayRefreshCycleDuration(nsecs_t* outRefreshDuration); + /* Enables or disables frame timestamp tracking. It is disabled by default * to avoid overhead during queue and dequeue for applications that don't * need the feature. If disabled, calls to getFrameTimestamps will fail. */ void enableFrameTimestamps(bool enable); + status_t getCompositorTiming( + nsecs_t* compositeDeadline, nsecs_t* compositeInterval, + nsecs_t* compositeToPresentLatency); + // See IGraphicBufferProducer::getFrameTimestamps status_t getFrameTimestamps(uint64_t frameNumber, nsecs_t* outRequestedPresentTime, nsecs_t* outAcquireTime, @@ -148,7 +154,6 @@ public: nsecs_t* outLastRefreshStartTime, nsecs_t* outGlCompositionDoneTime, nsecs_t* outDisplayPresentTime, nsecs_t* outDisplayRetireTime, nsecs_t* outDequeueReadyTime, nsecs_t* outReleaseTime); - status_t getDisplayRefreshCycleDuration(nsecs_t* outRefreshDuration); status_t getUniqueId(uint64_t* outId) const; @@ -157,6 +162,7 @@ protected: // Virtual for testing. virtual sp<ISurfaceComposer> composerService() const; + virtual nsecs_t now() const; private: // can't be copied @@ -204,10 +210,11 @@ private: int dispatchSetSurfaceDamage(va_list args); int dispatchSetSharedBufferMode(va_list args); int dispatchSetAutoRefresh(va_list args); + int dispatchGetDisplayRefreshCycleDuration(va_list args); int dispatchGetNextFrameId(va_list args); int dispatchEnableFrameTimestamps(va_list args); + int dispatchGetCompositorTiming(va_list args); int dispatchGetFrameTimestamps(va_list args); - int dispatchGetDisplayRefreshCycleDuration(va_list args); protected: virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd); diff --git a/libs/gui/FrameTimestamps.cpp b/libs/gui/FrameTimestamps.cpp index 73537bfc27..a6fa38aae5 100644 --- a/libs/gui/FrameTimestamps.cpp +++ b/libs/gui/FrameTimestamps.cpp @@ -235,6 +235,23 @@ void FrameEventHistory::dump(String8& outString) const { ProducerFrameEventHistory::~ProducerFrameEventHistory() = default; +nsecs_t ProducerFrameEventHistory::snapToNextTick( + nsecs_t timestamp, nsecs_t tickPhase, nsecs_t tickInterval) { + nsecs_t tickOffset = (tickPhase - timestamp) % tickInterval; + // Integer modulo rounds towards 0 and not -inf before taking the remainder, + // so adjust the offset if it is negative. + if (tickOffset < 0) { + tickOffset += tickInterval; + } + return timestamp + tickOffset; +} + +nsecs_t ProducerFrameEventHistory::getNextCompositeDeadline( + const nsecs_t now) const{ + return snapToNextTick( + now, mCompositorTiming.deadline, mCompositorTiming.interval); +} + void ProducerFrameEventHistory::updateAcquireFence( uint64_t frameNumber, std::shared_ptr<FenceTime>&& acquire) { FrameEvents* frame = getFrame(frameNumber, &mAcquireOffset); @@ -256,6 +273,8 @@ void ProducerFrameEventHistory::updateAcquireFence( void ProducerFrameEventHistory::applyDelta( const FrameEventHistoryDelta& delta) { + mCompositorTiming = delta.mCompositorTiming; + for (auto& d : delta.mDeltas) { // Avoid out-of-bounds access. if (d.mIndex >= mFrames.size()) { @@ -346,6 +365,11 @@ std::shared_ptr<FenceTime> ProducerFrameEventHistory::createFenceTime( ConsumerFrameEventHistory::~ConsumerFrameEventHistory() = default; +void ConsumerFrameEventHistory::initializeCompositorTiming( + const CompositorTiming& compositorTiming) { + mCompositorTiming = compositorTiming; +} + void ConsumerFrameEventHistory::addQueue(const NewFrameEventsEntry& newEntry) { // Overwrite all fields of the frame with default values unless set here. FrameEvents newTimestamps; @@ -393,7 +417,10 @@ void ConsumerFrameEventHistory::addPreComposition( void ConsumerFrameEventHistory::addPostComposition(uint64_t frameNumber, const std::shared_ptr<FenceTime>& gpuCompositionDone, - const std::shared_ptr<FenceTime>& displayPresent) { + const std::shared_ptr<FenceTime>& displayPresent, + const CompositorTiming& compositorTiming) { + mCompositorTiming = compositorTiming; + FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset); if (frame == nullptr) { ALOGE_IF(mProducerWantsEvents, @@ -450,6 +477,8 @@ void ConsumerFrameEventHistory::getFrameDelta( void ConsumerFrameEventHistory::getAndResetDelta( FrameEventHistoryDelta* delta) { + delta->mCompositorTiming = mCompositorTiming; + // Write these in order of frame number so that it is easy to // add them to a FenceTimeline in the proper order producer side. delta->mDeltas.reserve(mFramesDirty.size()); @@ -499,9 +528,8 @@ FrameEventsDelta::FrameEventsDelta( } } -size_t FrameEventsDelta::minFlattenedSize() { - constexpr size_t min = - sizeof(FrameEventsDelta::mFrameNumber) + +constexpr size_t FrameEventsDelta::minFlattenedSize() { + return sizeof(FrameEventsDelta::mFrameNumber) + sizeof(uint8_t) + // mIndex sizeof(uint8_t) + // mAddPostCompositeCalled sizeof(uint8_t) + // mAddRetireCalled @@ -512,7 +540,6 @@ size_t FrameEventsDelta::minFlattenedSize() { sizeof(FrameEventsDelta::mFirstRefreshStartTime) + sizeof(FrameEventsDelta::mLastRefreshStartTime) + sizeof(FrameEventsDelta::mDequeueReadyTime); - return min; } // Flattenable implementation @@ -618,6 +645,8 @@ status_t FrameEventsDelta::unflatten(void const*& buffer, size_t& size, FrameEventHistoryDelta& FrameEventHistoryDelta::operator=( FrameEventHistoryDelta&& src) { + mCompositorTiming = src.mCompositorTiming; + if (CC_UNLIKELY(!mDeltas.empty())) { ALOGE("FrameEventHistoryDelta: Clobbering history."); } @@ -626,8 +655,9 @@ FrameEventHistoryDelta& FrameEventHistoryDelta::operator=( return *this; } -size_t FrameEventHistoryDelta::minFlattenedSize() { - return sizeof(uint32_t); +constexpr size_t FrameEventHistoryDelta::minFlattenedSize() { + return sizeof(uint32_t) + // mDeltas.size() + sizeof(mCompositorTiming); } size_t FrameEventHistoryDelta::getFlattenedSize() const { @@ -654,6 +684,8 @@ status_t FrameEventHistoryDelta::flatten( return NO_MEMORY; } + FlattenableUtils::write(buffer, size, mCompositorTiming); + FlattenableUtils::write( buffer, size, static_cast<uint32_t>(mDeltas.size())); for (auto& d : mDeltas) { @@ -671,6 +703,8 @@ status_t FrameEventHistoryDelta::unflatten( return NO_MEMORY; } + FlattenableUtils::read(buffer, size, mCompositorTiming); + uint32_t deltaCount = 0; FlattenableUtils::read(buffer, size, deltaCount); if (deltaCount > FrameEventHistory::MAX_FRAME_HISTORY) { diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index d1f9f6a7bc..d285ef0ce9 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -100,6 +100,10 @@ sp<ISurfaceComposer> Surface::composerService() const { return ComposerService::getComposerService(); } +nsecs_t Surface::now() const { + return systemTime(); +} + sp<IGraphicBufferProducer> Surface::getIGraphicBufferProducer() const { return mGraphicBufferProducer; } @@ -142,11 +146,51 @@ status_t Surface::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, outTransformMatrix); } +status_t Surface::getDisplayRefreshCycleDuration(nsecs_t* outRefreshDuration) { + ATRACE_CALL(); + + DisplayStatInfo stats; + status_t err = composerService()->getDisplayStats(NULL, &stats); + + *outRefreshDuration = stats.vsyncPeriod; + + return NO_ERROR; +} + void Surface::enableFrameTimestamps(bool enable) { Mutex::Autolock lock(mMutex); + // If going from disabled to enabled, get the initial values for + // compositor and display timing. + if (!mEnableFrameTimestamps && enable) { + FrameEventHistoryDelta delta; + mGraphicBufferProducer->getFrameTimestamps(&delta); + mFrameEventHistory->applyDelta(delta); + } mEnableFrameTimestamps = enable; } +status_t Surface::getCompositorTiming( + nsecs_t* compositeDeadline, nsecs_t* compositeInterval, + nsecs_t* compositeToPresentLatency) { + Mutex::Autolock lock(mMutex); + if (!mEnableFrameTimestamps) { + return INVALID_OPERATION; + } + + if (compositeDeadline != nullptr) { + *compositeDeadline = + mFrameEventHistory->getNextCompositeDeadline(now()); + } + if (compositeInterval != nullptr) { + *compositeInterval = mFrameEventHistory->getCompositeInterval(); + } + if (compositeToPresentLatency != nullptr) { + *compositeToPresentLatency = + mFrameEventHistory->getCompositeToPresentLatency(); + } + return NO_ERROR; +} + static bool checkConsumerForUpdates( const FrameEvents* e, const uint64_t lastFrameNumber, const nsecs_t* outLatchTime, @@ -260,16 +304,6 @@ status_t Surface::getFrameTimestamps(uint64_t frameNumber, return NO_ERROR; } -status_t Surface::getDisplayRefreshCycleDuration(nsecs_t* outRefreshDuration) { - ATRACE_CALL(); - - DisplayStatInfo stats; - status_t err = composerService()->getDisplayStats(NULL, &stats); - - *outRefreshDuration = stats.vsyncPeriod; - - return NO_ERROR; -} int Surface::hook_setSwapInterval(ANativeWindow* window, int interval) { Surface* c = getSelf(window); @@ -831,18 +865,21 @@ int Surface::perform(int operation, va_list args) case NATIVE_WINDOW_SET_AUTO_REFRESH: res = dispatchSetAutoRefresh(args); break; + case NATIVE_WINDOW_GET_REFRESH_CYCLE_DURATION: + res = dispatchGetDisplayRefreshCycleDuration(args); + break; case NATIVE_WINDOW_GET_NEXT_FRAME_ID: res = dispatchGetNextFrameId(args); break; case NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS: res = dispatchEnableFrameTimestamps(args); break; + case NATIVE_WINDOW_GET_COMPOSITOR_TIMING: + res = dispatchGetCompositorTiming(args); + break; case NATIVE_WINDOW_GET_FRAME_TIMESTAMPS: res = dispatchGetFrameTimestamps(args); break; - case NATIVE_WINDOW_GET_REFRESH_CYCLE_DURATION: - res = dispatchGetDisplayRefreshCycleDuration(args); - break; default: res = NAME_NOT_FOUND; break; @@ -963,6 +1000,11 @@ int Surface::dispatchSetAutoRefresh(va_list args) { return setAutoRefresh(autoRefresh); } +int Surface::dispatchGetDisplayRefreshCycleDuration(va_list args) { + nsecs_t* outRefreshDuration = va_arg(args, int64_t*); + return getDisplayRefreshCycleDuration(outRefreshDuration); +} + int Surface::dispatchGetNextFrameId(va_list args) { uint64_t* nextFrameId = va_arg(args, uint64_t*); *nextFrameId = getNextFrameNumber(); @@ -975,6 +1017,14 @@ int Surface::dispatchEnableFrameTimestamps(va_list args) { return NO_ERROR; } +int Surface::dispatchGetCompositorTiming(va_list args) { + nsecs_t* compositeDeadline = va_arg(args, int64_t*); + nsecs_t* compositeInterval = va_arg(args, int64_t*); + nsecs_t* compositeToPresentLatency = va_arg(args, int64_t*); + return getCompositorTiming(compositeDeadline, compositeInterval, + compositeToPresentLatency); +} + int Surface::dispatchGetFrameTimestamps(va_list args) { uint64_t frameId = va_arg(args, uint64_t); nsecs_t* outRequestedPresentTime = va_arg(args, int64_t*); @@ -994,11 +1044,6 @@ int Surface::dispatchGetFrameTimestamps(va_list args) { outDisplayRetireTime, outDequeueReadyTime, outReleaseTime); } -int Surface::dispatchGetDisplayRefreshCycleDuration(va_list args) { - nsecs_t* outRefreshDuration = va_arg(args, int64_t*); - return getDisplayRefreshCycleDuration(outRefreshDuration); -} - int Surface::connect(int api) { static sp<IProducerListener> listener = new DummyProducerListener(); return connect(api, listener); diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 3f56665a61..52980270f2 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -479,8 +479,17 @@ public: return mFakeSurfaceComposer; } + nsecs_t now() const override { + return mNow; + } + + void setNow(nsecs_t now) { + mNow = now; + } + public: sp<FakeSurfaceComposer> mFakeSurfaceComposer; + nsecs_t mNow = 0; // mFrameEventHistory owns the instance of FakeProducerFrameEventHistory, // but this raw pointer gives access to test functionality. @@ -500,10 +509,12 @@ protected: struct RefreshEvents { RefreshEvents(FenceToFenceTimeMap& fenceMap, nsecs_t refreshStart) - : mFenceMap(fenceMap), - kStartTime(refreshStart + 1), - kGpuCompositionDoneTime(refreshStart + 2), - kPresentTime(refreshStart + 3) {} + : mFenceMap(fenceMap), + kCompositorTiming( + {refreshStart, refreshStart + 1, refreshStart + 2 }), + kStartTime(refreshStart + 3), + kGpuCompositionDoneTime(refreshStart + 4), + kPresentTime(refreshStart + 5) {} void signalPostCompositeFences() { mFenceMap.signalAllForTest( @@ -516,6 +527,8 @@ protected: FenceAndFenceTime mGpuCompositionDone { mFenceMap }; FenceAndFenceTime mPresent { mFenceMap }; + const CompositorTiming kCompositorTiming; + const nsecs_t kStartTime; const nsecs_t kGpuCompositionDoneTime; const nsecs_t kPresentTime; @@ -592,6 +605,12 @@ protected: 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); @@ -681,7 +700,8 @@ protected: oldFrame->mRefreshes[2].mGpuCompositionDone.mFenceTime : FenceTime::NO_FENCE; mCfeh->addPostComposition(nOldFrame, gpuDoneFenceTime, - oldFrame->mRefreshes[2].mPresent.mFenceTime); + oldFrame->mRefreshes[2].mPresent.mFenceTime, + oldFrame->mRefreshes[2].kCompositorTiming); } // Latch the new frame. @@ -698,7 +718,8 @@ protected: std::shared_ptr<FenceTime>(oldFrame->mRelease.mFenceTime)); } mCfeh->addPostComposition(nNewFrame, gpuDoneFenceTime, - newFrame->mRefreshes[0].mPresent.mFenceTime); + newFrame->mRefreshes[0].mPresent.mFenceTime, + newFrame->mRefreshes[0].kCompositorTiming); // Retire the previous buffer just after compositing the new buffer. if (oldFrame != nullptr) { @@ -710,7 +731,8 @@ protected: newFrame->mRefreshes[1].mGpuCompositionDone.mFenceTime : FenceTime::NO_FENCE; mCfeh->addPostComposition(nNewFrame, gpuDoneFenceTime, - newFrame->mRefreshes[1].mPresent.mFenceTime); + newFrame->mRefreshes[1].mPresent.mFenceTime, + newFrame->mRefreshes[1].kCompositorTiming); } void QueryPresentRetireSupported( @@ -740,7 +762,8 @@ protected: int64_t outDequeueReadyTime = -1; int64_t outReleaseTime = -1; - FrameEvents mFrames[2] { { mFenceMap, 1000 }, { mFenceMap, 2000 } }; + FrameEvents mFrames[3] { + { mFenceMap, 1000 }, { mFenceMap, 2000 }, { mFenceMap, 3000 } }; }; @@ -773,25 +796,55 @@ TEST_F(GetFrameTimestampsTest, DefaultDisabled) { 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(0, mFakeConsumer->mGetFrameTimestampsCount); + 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(1, mFakeConsumer->mGetFrameTimestampsCount); + EXPECT_EQ(2, mFakeConsumer->mGetFrameTimestampsCount); NewFrameEventsEntry f1; f1.frameNumber = 1; @@ -808,13 +861,13 @@ TEST_F(GetFrameTimestampsTest, EnabledSimple) { 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); + 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. - int result = getAllFrameTimestamps(fId1); + result = getAllFrameTimestamps(fId1); EXPECT_EQ(NO_ERROR, result); - EXPECT_EQ(3, mFakeConsumer->mGetFrameTimestampsCount); + EXPECT_EQ(4, mFakeConsumer->mGetFrameTimestampsCount); } void GetFrameTimestampsTest::QueryPresentRetireSupported( @@ -842,6 +895,234 @@ TEST_F(GetFrameTimestampsTest, QueryRetireSupported) { QueryPresentRetireSupported(false, true); } +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); + + const uint64_t fId1 = getNextFrameId(); + 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); + + const uint64_t fId2 = getNextFrameId(); + 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); + + const uint64_t fId1 = getNextFrameId(); + 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); + + const uint64_t fId2 = getNextFrameId(); + 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. diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h index 6485ae5c1d..6572cab466 100644 --- a/opengl/include/EGL/eglext.h +++ b/opengl/include/EGL/eglext.h @@ -632,24 +632,31 @@ typedef EGLClientBuffer (EGLAPIENTRYP PFNEGLCREATENATIVECLIENTBUFFERANDROID) (co #ifndef EGL_ANDROID_get_frame_timestamps #define EGL_ANDROID_get_frame_timestamps 1 #define EGL_TIMESTAMPS_ANDROID 0x314D -#define EGL_REQUESTED_PRESENT_TIME_ANDROID 0x314E -#define EGL_RENDERING_COMPLETE_TIME_ANDROID 0x314F -#define EGL_COMPOSITION_LATCH_TIME_ANDROID 0x3150 -#define EGL_FIRST_COMPOSITION_START_TIME_ANDROID 0x3151 -#define EGL_LAST_COMPOSITION_START_TIME_ANDROID 0x3152 -#define EGL_FIRST_COMPOSITION_FINISHED_TIME_ANDROID 0x3153 -#define EGL_DISPLAY_PRESENT_TIME_ANDROID 0x3154 -#define EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3155 -#define EGL_DEQUEUE_READY_TIME_ANDROID 0x3156 -#define EGL_READS_DONE_TIME_ANDROID 0x3157 +#define EGL_COMPOSITE_DEADLINE_ANDROID 0x314E +#define EGL_COMPOSITE_INTERVAL_ANDROID 0x314F +#define EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID 0x3150 +#define EGL_REQUESTED_PRESENT_TIME_ANDROID 0x3151 +#define EGL_RENDERING_COMPLETE_TIME_ANDROID 0x3152 +#define EGL_COMPOSITION_LATCH_TIME_ANDROID 0x3153 +#define EGL_FIRST_COMPOSITION_START_TIME_ANDROID 0x3154 +#define EGL_LAST_COMPOSITION_START_TIME_ANDROID 0x3155 +#define EGL_FIRST_COMPOSITION_FINISHED_TIME_ANDROID 0x3156 +#define EGL_DISPLAY_PRESENT_TIME_ANDROID 0x3157 +#define EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3158 +#define EGL_DEQUEUE_READY_TIME_ANDROID 0x3159 +#define EGL_READS_DONE_TIME_ANDROID 0x315A #ifdef EGL_EGLEXT_PROTOTYPES EGLAPI EGLBoolean eglGetNextFrameIdANDROID(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR *frameId); +EGLAPI EGLBoolean eglGetCompositorTimingANDROID(EGLDisplay dpy, EGLSurface surface, EGLint numTimestamps, const EGLint *names, EGLnsecsANDROID *values); +EGLAPI EGLBoolean eglGetCompositorTimingSupportedANDROID(EGLDisplay dpy, EGLSurface surface, EGLint name); EGLAPI EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values); -EGLAPI EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface, EGLint timestamp); +EGLAPI EGLBoolean eglGetFrameTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface, EGLint timestamp); #else typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETNEXTFRAMEIDANDROID) (EGLDisplay dpy, EGLSurface surface, EGLuint64KHR *frameId); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETCOMPOSITORTIMINGANDROID) (EGLDisplay dpy, EGLSurface surface, EGLint numTimestamps, const EGLint *names, EGLnsecsANDROID *values); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETCOMPOSITORTIMINGSUPPORTEDANDROID) (EGLDisplay dpy, EGLSurface surface, EGLint name); typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETFRAMETIMESTAMPSANDROID) (EGLDisplay dpy, EGLSurface surface, EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values); -typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYTIMESTAMPSUPPORTEDANDROID) (EGLDisplay dpy, EGLSurface surface, EGLint timestamp); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETFRAMETIMESTAMPSUPPORTEDANDROID) (EGLDisplay dpy, EGLSurface surface, EGLint timestamp); #endif #endif diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp index 2d02b72fb8..4681b89aa6 100644 --- a/opengl/libs/EGL/eglApi.cpp +++ b/opengl/libs/EGL/eglApi.cpp @@ -56,8 +56,6 @@ using namespace android; -#define ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS 0 - // ---------------------------------------------------------------------------- namespace android { @@ -88,9 +86,7 @@ extern char const * const gBuiltinExtensionString = "EGL_KHR_swap_buffers_with_damage " "EGL_ANDROID_create_native_client_buffer " "EGL_ANDROID_front_buffer_auto_refresh " -#if ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS "EGL_ANDROID_get_frame_timestamps " -#endif ; extern char const * const gExtensionString = "EGL_KHR_image " // mandatory @@ -218,10 +214,14 @@ static const extention_map_t sExtensionMap[] = { // EGL_ANDROID_get_frame_timestamps { "eglGetNextFrameIdANDROID", (__eglMustCastToProperFunctionPointerType)&eglGetNextFrameIdANDROID }, + { "eglGetCompositorTimingANDROID", + (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingANDROID }, + { "eglGetCompositorTimingSupportedANDROID", + (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingSupportedANDROID }, { "eglGetFrameTimestampsANDROID", (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampsANDROID }, - { "eglQueryTimestampSupportedANDROID", - (__eglMustCastToProperFunctionPointerType)&eglQueryTimestampSupportedANDROID }, + { "eglGetFrameTimestampSupportedANDROID", + (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampSupportedANDROID }, }; /* @@ -1242,7 +1242,6 @@ EGLBoolean eglSurfaceAttrib( setError(EGL_BAD_SURFACE, EGL_FALSE); } -#if ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS if (attribute == EGL_TIMESTAMPS_ANDROID) { if (!s->win.get()) { return setError(EGL_BAD_SURFACE, EGL_FALSE); @@ -1252,7 +1251,6 @@ EGLBoolean eglSurfaceAttrib( return (err == NO_ERROR) ? EGL_TRUE : setError(EGL_BAD_SURFACE, EGL_FALSE); } -#endif if (s->cnx->egl.eglSurfaceAttrib) { return s->cnx->egl.eglSurfaceAttrib( @@ -2084,6 +2082,95 @@ EGLBoolean eglGetNextFrameIdANDROID(EGLDisplay dpy, EGLSurface surface, return EGL_TRUE; } +EGLBoolean eglGetCompositorTimingANDROID(EGLDisplay dpy, EGLSurface surface, + EGLint numTimestamps, const EGLint *names, EGLnsecsANDROID *values) +{ + clearError(); + + const egl_display_ptr dp = validate_display(dpy); + if (!dp) { + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + } + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) { + return setError(EGL_BAD_SURFACE, EGL_FALSE); + } + + egl_surface_t const * const s = get_surface(surface); + + if (!s->win.get()) { + return setError(EGL_BAD_SURFACE, EGL_FALSE); + } + + nsecs_t* compositeDeadline = nullptr; + nsecs_t* compositeInterval = nullptr; + nsecs_t* compositeToPresentLatency = nullptr; + + for (int i = 0; i < numTimestamps; i++) { + switch (names[i]) { + case EGL_COMPOSITE_DEADLINE_ANDROID: + compositeDeadline = &values[i]; + break; + case EGL_COMPOSITE_INTERVAL_ANDROID: + compositeInterval = &values[i]; + break; + case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID: + compositeToPresentLatency = &values[i]; + break; + default: + return setError(EGL_BAD_PARAMETER, EGL_FALSE); + } + } + + status_t ret = native_window_get_compositor_timing(s->win.get(), + compositeDeadline, compositeInterval, compositeToPresentLatency); + + switch (ret) { + case NO_ERROR: + return EGL_TRUE; + case INVALID_OPERATION: + return setError(EGL_BAD_SURFACE, EGL_FALSE); + default: + // This should not happen. Return an error that is not in the spec + // so it's obvious something is very wrong. + ALOGE("eglGetCompositorTiming: Unexpected error."); + return setError(EGL_NOT_INITIALIZED, EGL_FALSE); + } +} + +EGLBoolean eglGetCompositorTimingSupportedANDROID( + EGLDisplay dpy, EGLSurface surface, EGLint name) +{ + clearError(); + + const egl_display_ptr dp = validate_display(dpy); + if (!dp) { + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + } + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) { + return setError(EGL_BAD_SURFACE, EGL_FALSE); + } + + egl_surface_t const * const s = get_surface(surface); + + ANativeWindow* window = s->win.get(); + if (!window) { + return setError(EGL_BAD_SURFACE, EGL_FALSE); + } + + switch (name) { + case EGL_COMPOSITE_DEADLINE_ANDROID: + case EGL_COMPOSITE_INTERVAL_ANDROID: + case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID: + return EGL_TRUE; + default: + return EGL_FALSE; + } +} + EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values) @@ -2176,8 +2263,8 @@ EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface, } } -EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface, - EGLint timestamp) +EGLBoolean eglGetFrameTimestampSupportedANDROID( + EGLDisplay dpy, EGLSurface surface, EGLint timestamp) { clearError(); @@ -2199,7 +2286,9 @@ EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface, } switch (timestamp) { -#if ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS + case EGL_COMPOSITE_DEADLINE_ANDROID: + case EGL_COMPOSITE_INTERVAL_ANDROID: + case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID: case EGL_REQUESTED_PRESENT_TIME_ANDROID: case EGL_RENDERING_COMPLETE_TIME_ANDROID: case EGL_COMPOSITION_LATCH_TIME_ANDROID: @@ -2221,7 +2310,6 @@ EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface, NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_RETIRE, &value); return value == 0 ? EGL_FALSE : EGL_TRUE; } -#endif default: return EGL_FALSE; } diff --git a/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt b/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt index f24d634963..d0ed8e172f 100644 --- a/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt +++ b/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt @@ -8,11 +8,19 @@ Name Strings Contributors + Brian Anderson + Dan Stoza Pablo Ceballos + Jesse Hall + Fabien Sanglard Contact + Brian Anderson, Google Inc. (brianderson 'at' google.com) + Dan Stoza, Google Inc. (stoza 'at' google.com) Pablo Ceballos, Google Inc. (pceballos 'at' google.com) + Jesse Hall, Google Inc. (jessehall 'at' google.com) + Fabien Sanglard, Google Inc. (sanglardf 'at' google.com) Status @@ -20,7 +28,7 @@ Status Version - Version 1, May 31, 2016 + Version 1, January 13, 2017 Number @@ -60,26 +68,33 @@ New Procedures and Functions EGLBoolean eglGetNextFrameIdANDROID(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR *frameId); + EGLBoolean eglGetCompositorTimingANDROID(EGLDisplay dpy, + EGLSurface surface, EGLint numTimestamps, + const EGLint *names, EGLnsecsANDROID *values); + EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values); - EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface - surface, EGLint timestamp); + EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, + EGLSurface surface, EGLint timestamp); New Tokens EGL_TIMESTAMPS_ANDROID 0x314D - EGL_REQUESTED_PRESENT_TIME_ANDROID 0x314E - EGL_RENDERING_COMPLETE_TIME_ANDROID 0x314F - EGL_COMPOSITION_LATCH_TIME_ANDROID 0x3150 - EGL_FIRST_COMPOSITION_START_TIME_ANDROID 0x3151 - EGL_LAST_COMPOSITION_START_TIME_ANDROID 0x3152 - EGL_FIRST_COMPOSITION_FINISHED_TIME_ANDROID 0x3153 - EGL_DISPLAY_PRESENT_TIME_ANDROID 0x3154 - EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3155 - EGL_DEQUEUE_READY_TIME_ANDROID 0x3156 - EGL_READS_DONE_TIME_ANDROID 0x3157 + EGL_COMPOSITE_DEADLINE_ANDROID 0x314E + EGL_COMPOSITE_INTERVAL_ANDROID 0x314F + EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID 0x3150 + EGL_REQUESTED_PRESENT_TIME_ANDROID 0x3151 + EGL_RENDERING_COMPLETE_TIME_ANDROID 0x3152 + EGL_COMPOSITION_LATCH_TIME_ANDROID 0x3153 + EGL_FIRST_COMPOSITION_START_TIME_ANDROID 0x3154 + EGL_LAST_COMPOSITION_START_TIME_ANDROID 0x3155 + EGL_FIRST_COMPOSITION_FINISHED_TIME_ANDROID 0x3156 + EGL_DISPLAY_PRESENT_TIME_ANDROID 0x3157 + EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3158 + EGL_DEQUEUE_READY_TIME_ANDROID 0x3159 + EGL_READS_DONE_TIME_ANDROID 0x315A Add to the list of supported tokens for eglSurfaceAttrib in section 3.5.6 "Surface Attributes", page 43: @@ -89,7 +104,6 @@ Add to the list of supported tokens for eglSurfaceAttrib in section 3.5.6 enables timestamp collection, while a value of EGL_FALSE disables it. The initial value is false. If surface is not a window surface this has no effect. - Changes to Chapter 3 of the EGL 1.5 Specification (EGL Functions and Errors) Add a new subsection under Section 3, @@ -108,6 +122,29 @@ Changes to Chapter 3 of the EGL 1.5 Specification (EGL Functions and Errors) The function + EGLBoolean eglGetCompositorTimingANDROID(EGLDisplay dpy, + EGLSurface surface, EGLint numTimestamps, + const EGLint *names, EGLnsecsANDROID *values); + + allows querying anticipated timestamps and durations related to the + composition and display of a window surface. The values are not associated + with a particular frame and can be retrieved before the first swap. + + The eglGetCompositorTimingANDROID function takes an array of names to + query and returns their values in the corresponding indices of the values + array. The possible names that can be queried are: + - EGL_COMPOSITE_DEADLINE_ANDROID - The timestamp of the next time the + compositor will begin composition. This is effectively the deadline + for when the compositor must receive a newly queued frame. + - EGL_COMPOSITE_INTERVAL_ANDROID - The time delta between subsequent + composition events. + - EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID - The time delta between + the start of composition and the expected present time of that + composition. This can be used to estimate the latency of the + actual present time. + + The function + EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values); @@ -124,6 +161,12 @@ Changes to Chapter 3 of the EGL 1.5 Specification (EGL Functions and Errors) supported will generate an EGL_BAD_PARAMETER error. If any error is generated the function will return EGL_FALSE. + The application can poll for the timestamp of particular events by calling + eglGetFrameTimestamps over and over without needing to call any other EGL + function between calls. This is true even for the most recently swapped + frame. eglGetFrameTimestamps is thread safe and can be called from a + different thread than the swapping thread. + The eglGetFrameTimestampsANDROID function takes an array of timestamps to query and returns timestamps in the corresponding indices of the values array. The possible timestamps that can be queried are: @@ -162,12 +205,19 @@ Changes to Chapter 3 of the EGL 1.5 Specification (EGL Functions and Errors) purpose of display/composition were completed for this frame. Not all implementations may support all of the above timestamp queries. The - function + functions - EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface - surface, EGLint timestamp); + EGLBoolean eglGetCompositorTimingSupportedANDROID(EGLDisplay dpy, + EGLSurface surface, EGLint name); - allows querying which timestamps are supported on the implementation." + and + + EGLBoolean eglGetFrameTimestampsSupportedANDROID(EGLDisplay dpy, + EGLSurface surface, EGLint timestamp); + + allows querying which values are supported by the implementations of + eglGetCompositoTimingANDROID and eglGetFrameTimestampsSupportedANDROID + respectively." Issues @@ -175,17 +225,21 @@ Issues Revision History -#1 (Pablo Ceballos, May 31, 2016) - - Initial draft. +#5 (Brian Anderson, January 13, 2017) + - Add eglGetCompositorTimingANDROID. -#2 (Brian Anderson, July 22, 2016) - - Replace EGL_QUEUE_TIME_ANDROID with EGL_REQUESTED_PRESENT_TIME_ANDROID. - - Add DISPLAY_PRESENT_TIME_ANDROID. +#4 (Brian Anderson, January 10, 2017) + - Use an absolute frameId rather than a relative framesAgo. #3 (Brian Anderson, November 30, 2016) - Add EGL_COMPOSITION_LATCH_TIME_ANDROID, EGL_LAST_COMPOSITION_START_TIME_ANDROID, and EGL_DEQUEUE_READY_TIME_ANDROID. -#4 (Brian Anderson, January 10, 2017) - - Use an absolute frameId rather than a relative framesAgo. +#2 (Brian Anderson, July 22, 2016) + - Replace EGL_QUEUE_TIME_ANDROID with EGL_REQUESTED_PRESENT_TIME_ANDROID. + - Add DISPLAY_PRESENT_TIME_ANDROID. + +#1 (Pablo Ceballos, May 31, 2016) + - Initial draft. + diff --git a/opengl/specs/README b/opengl/specs/README index 1853214917..0c49023f08 100644 --- a/opengl/specs/README +++ b/opengl/specs/README @@ -20,14 +20,17 @@ for use by Android extensions. 0x314B EGL_IMAGE_CROP_BOTTOM_ANDROID (EGL_ANDROID_image_crop) 0x314C EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID (EGL_ANDROID_front_buffer_auto_refresh) 0x314D EGL_TIMESTAMPS_ANDROID (EGL_ANDROID_get_frame_timestamps) -0x314E EGL_REQUESTED_PRESENT_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) -0x314F EGL_RENDERING_COMPLETE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) -0x3150 EGL_COMPOSITION_LATCH_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) -0x3151 EGL_FIRST_COMPOSITION_START_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) -0x3152 EGL_LAST_COMPOSITION_START_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) -0x3153 EGL_FIRST_COMPOSITION_FINISHED_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) -0x3154 EGL_DISPLAY_PRESENT_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) -0x3155 EGL_DISPLAY_RETIRE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) -0x3156 EGL_DEQUEUE_READY_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) -0x3157 EGL_READS_DONE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) -0x3158 - 0x315F (unused) +0x314E EGL_COMPOSITE_DEADLINE_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x314F EGL_COMPOSITE_INTERVAL_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x3150 EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x3151 EGL_REQUESTED_PRESENT_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x3152 EGL_RENDERING_COMPLETE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x3153 EGL_COMPOSITION_LATCH_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x3154 EGL_FIRST_COMPOSITION_START_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x3155 EGL_LAST_COMPOSITION_START_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x3156 EGL_FIRST_COMPOSITION_FINISHED_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x3157 EGL_DISPLAY_PRESENT_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x3158 EGL_DISPLAY_RETIRE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x3159 EGL_DEQUEUE_READY_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x315A EGL_READS_DONE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x315B - 0x315F (unused) diff --git a/services/audiomanager/IPlayer.cpp b/services/audiomanager/IPlayer.cpp index 47edc4bd31..e8a9c3472f 100644 --- a/services/audiomanager/IPlayer.cpp +++ b/services/audiomanager/IPlayer.cpp @@ -35,6 +35,7 @@ enum { SET_VOLUME = IBinder::FIRST_CALL_TRANSACTION + 3, SET_PAN = IBinder::FIRST_CALL_TRANSACTION + 4, SET_START_DELAY_MS = IBinder::FIRST_CALL_TRANSACTION + 5, + APPLY_VOLUME_SHAPER = IBinder::FIRST_CALL_TRANSACTION + 6, }; class BpPlayer : public BpInterface<IPlayer> @@ -88,6 +89,36 @@ public: data.writeInt32(delayMs); remote()->transact(SET_START_DELAY_MS, data, &reply); } + + virtual void applyVolumeShaper( + const sp<VolumeShaper::Configuration>& configuration, + const sp<VolumeShaper::Operation>& operation) { + Parcel data, reply; + data.writeInterfaceToken(IPlayer::getInterfaceDescriptor()); + + status_t status = configuration.get() == nullptr + ? data.writeInt32(0) + : data.writeInt32(1) + ?: configuration->writeToParcel(&data); + if (status != NO_ERROR) { + ALOGW("applyVolumeShaper failed configuration parceling: %d", status); + return; // ignore error + } + + status = operation.get() == nullptr + ? status = data.writeInt32(0) + : data.writeInt32(1) + ?: operation->writeToParcel(&data); + if (status != NO_ERROR) { + ALOGW("applyVolumeShaper failed operation parceling: %d", status); + return; // ignore error + } + + status = remote()->transact(APPLY_VOLUME_SHAPER, data, &reply); + + ALOGW_IF(status != NO_ERROR, "applyVolumeShaper failed transact: %d", status); + return; // one way transaction, ignore error + } }; IMPLEMENT_META_INTERFACE(Player, "android.media.IPlayer"); @@ -128,6 +159,28 @@ status_t BnPlayer::onTransact( setStartDelayMs(data.readInt32()); return NO_ERROR; } break; + case APPLY_VOLUME_SHAPER: { + CHECK_INTERFACE(IPlayer, data, reply); + sp<VolumeShaper::Configuration> configuration; + sp<VolumeShaper::Operation> operation; + + int32_t present; + status_t status = data.readInt32(&present); + if (status == NO_ERROR && present != 0) { + configuration = new VolumeShaper::Configuration(); + status = configuration->readFromParcel(data); + } + status = status ?: data.readInt32(&present); + if (status == NO_ERROR && present != 0) { + operation = new VolumeShaper::Operation(); + status = operation->readFromParcel(data); + } + if (status == NO_ERROR) { + // one way transaction, no error returned + applyVolumeShaper(configuration, operation); + } + return NO_ERROR; + } break; default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 6e0a489acf..a854aec4bd 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -155,6 +155,10 @@ Layer::Layer(SurfaceFlinger* flinger, const sp<Client>& client, flinger->getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY); #endif mFrameTracker.setDisplayRefreshPeriod(displayPeriod); + + CompositorTiming compositorTiming; + flinger->getCompositorTiming(&compositorTiming); + mFrameEventHistory.initializeCompositorTiming(compositorTiming); } void Layer::onFirstRef() { @@ -1882,10 +1886,10 @@ bool Layer::onPreComposition(nsecs_t refreshStartTime) { return mQueuedFrames > 0 || mSidebandStreamChanged || mAutoRefresh; } -bool Layer::onPostComposition( - const std::shared_ptr<FenceTime>& glDoneFence, +bool Layer::onPostComposition(const std::shared_ptr<FenceTime>& glDoneFence, const std::shared_ptr<FenceTime>& presentFence, - const std::shared_ptr<FenceTime>& retireFence) { + const std::shared_ptr<FenceTime>& retireFence, + const CompositorTiming& compositorTiming) { mAcquireTimeline.updateSignalTimes(); mReleaseTimeline.updateSignalTimes(); @@ -1898,7 +1902,7 @@ bool Layer::onPostComposition( { Mutex::Autolock lock(mFrameEventHistoryMutex); mFrameEventHistory.addPostComposition(mCurrentFrameNumber, - glDoneFence, presentFence); + glDoneFence, presentFence, compositorTiming); mFrameEventHistory.addRetire(mPreviousFrameNumber, retireFence); } diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 12166a8655..8227daec8d 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -296,10 +296,10 @@ public: * called after composition. * returns true if the layer latched a new buffer this frame. */ - bool onPostComposition( - const std::shared_ptr<FenceTime>& glDoneFence, + bool onPostComposition(const std::shared_ptr<FenceTime>& glDoneFence, const std::shared_ptr<FenceTime>& presentFence, - const std::shared_ptr<FenceTime>& retireFence); + const std::shared_ptr<FenceTime>& retireFence, + const CompositorTiming& compositorTiming); #ifdef USE_HWC2 // If a buffer was replaced this frame, release the former buffer diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index b39692c587..c21c81676b 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -129,7 +129,7 @@ static int64_t vsyncPhaseOffsetNs = getInt64< &ISurfaceFlingerConfigs::vsyncEventPhaseOffsetNs>(1000000); // This is the phase offset at which SurfaceFlinger's composition runs. -static const int64_t sfVsyncPhaseOffsetNs = SF_VSYNC_EVENT_PHASE_OFFSET_NS; +static constexpr int64_t sfVsyncPhaseOffsetNs = SF_VSYNC_EVENT_PHASE_OFFSET_NS; // --------------------------------------------------------------------------- @@ -1116,6 +1116,11 @@ void SurfaceFlinger::onVSyncReceived(HWComposer* composer, int32_t type, } } +void SurfaceFlinger::getCompositorTiming(CompositorTiming* compositorTiming) { + std::lock_guard<std::mutex> lock(mCompositeTimingLock); + *compositorTiming = mCompositorTiming; +} + void SurfaceFlinger::onHotplugReceived(int32_t disp, bool connected) { ALOGV("onHotplugReceived(%d, %s)", disp, connected ? "true" : "false"); if (disp == DisplayDevice::DISPLAY_PRIMARY) { @@ -1255,7 +1260,8 @@ void SurfaceFlinger::onMessageReceived(int32_t what) { bool frameMissed = !mHadClientComposition && mPreviousPresentFence != Fence::NO_FENCE && - mPreviousPresentFence->getSignalTime() == INT64_MAX; + (mPreviousPresentFence->getSignalTime() == + Fence::SIGNAL_TIME_PENDING); ATRACE_INT("FrameMissed", static_cast<int>(frameMissed)); if (mPropagateBackpressure && frameMissed) { signalLayerUpdate(); @@ -1304,7 +1310,7 @@ void SurfaceFlinger::handleMessageRefresh() { setUpHWComposer(); doDebugFlashRegions(); doComposition(); - postComposition(); + postComposition(refreshStartTime); mPreviousPresentFence = mHwc->getPresentFence(HWC_DISPLAY_PRIMARY); @@ -1379,7 +1385,61 @@ void SurfaceFlinger::preComposition(nsecs_t refreshStartTime) } } -void SurfaceFlinger::postComposition() +void SurfaceFlinger::updateCompositorTiming( + nsecs_t vsyncPhase, nsecs_t vsyncInterval, nsecs_t compositeTime, + std::shared_ptr<FenceTime>& presentFenceTime) { + // Update queue of past composite+present times and determine the + // most recently known composite to present latency. + mCompositePresentTimes.push({compositeTime, presentFenceTime}); + nsecs_t compositeToPresentLatency = -1; + while (!mCompositePresentTimes.empty()) { + CompositePresentTime& cpt = mCompositePresentTimes.front(); + // Cached values should have been updated before calling this method, + // which helps avoid duplicate syscalls. + nsecs_t displayTime = cpt.display->getCachedSignalTime(); + if (displayTime == Fence::SIGNAL_TIME_PENDING) { + break; + } + compositeToPresentLatency = displayTime - cpt.composite; + mCompositePresentTimes.pop(); + } + + // Don't let mCompositePresentTimes grow unbounded, just in case. + while (mCompositePresentTimes.size() > 16) { + mCompositePresentTimes.pop(); + } + + // Integer division and modulo round toward 0 not -inf, so we need to + // treat negative and positive offsets differently. + nsecs_t idealLatency = (sfVsyncPhaseOffsetNs >= 0) ? + (vsyncInterval - (sfVsyncPhaseOffsetNs % vsyncInterval)) : + ((-sfVsyncPhaseOffsetNs) % vsyncInterval); + + // Snap the latency to a value that removes scheduling jitter from the + // composition and present times, which often have >1ms of jitter. + // Reducing jitter is important if an app attempts to extrapolate + // something (such as user input) to an accurate diasplay time. + // Snapping also allows an app to precisely calculate sfVsyncPhaseOffsetNs + // with (presentLatency % interval). + nsecs_t snappedCompositeToPresentLatency = -1; + if (compositeToPresentLatency >= 0) { + nsecs_t bias = vsyncInterval / 2; + int64_t extraVsyncs = + (compositeToPresentLatency - idealLatency + bias) / + vsyncInterval; + nsecs_t extraLatency = extraVsyncs * vsyncInterval; + snappedCompositeToPresentLatency = idealLatency + extraLatency; + } + + std::lock_guard<std::mutex> lock(mCompositeTimingLock); + mCompositorTiming.deadline = vsyncPhase - idealLatency; + mCompositorTiming.interval = vsyncInterval; + if (snappedCompositeToPresentLatency >= 0) { + mCompositorTiming.presentLatency = snappedCompositeToPresentLatency; + } +} + +void SurfaceFlinger::postComposition(nsecs_t refreshStartTime) { ATRACE_CALL(); ALOGV("postComposition"); @@ -1414,9 +1474,19 @@ void SurfaceFlinger::postComposition() } else { retireFenceTime = &displayFenceTime; } + + nsecs_t vsyncPhase = mPrimaryDispSync.computeNextRefresh(0); + nsecs_t vsyncInterval = mPrimaryDispSync.getPeriod(); + + // We use the refreshStartTime which might be sampled a little later than + // when we started doing work for this frame, but that should be okay + // since updateCompositorTiming has snapping logic. + updateCompositorTiming( + vsyncPhase, vsyncInterval, refreshStartTime, displayFenceTime); + mDrawingState.traverseInZOrder([&](Layer* layer) { bool frameLatched = layer->onPostComposition(glCompositionDoneFenceTime, - *presentFenceTime, *retireFenceTime); + *presentFenceTime, *retireFenceTime, mCompositorTiming); if (frameLatched) { recordBufferingStats(layer->getName().string(), layer->getOccupancyHistory(false)); @@ -1461,9 +1531,8 @@ void SurfaceFlinger::postComposition() if (mHasPoweredOff) { mHasPoweredOff = false; } else { - nsecs_t period = mPrimaryDispSync.getPeriod(); nsecs_t elapsedTime = currentTime - mLastSwapTime; - size_t numPeriods = static_cast<size_t>(elapsedTime / period); + size_t numPeriods = static_cast<size_t>(elapsedTime / vsyncInterval); if (numPeriods < NUM_BUCKETS - 1) { mFrameBuckets[numPeriods] += elapsedTime; } else { @@ -2866,6 +2935,11 @@ void SurfaceFlinger::onInitializeDisplays() { const auto& activeConfig = mHwc->getActiveConfig(HWC_DISPLAY_PRIMARY); const nsecs_t period = activeConfig->getVsyncPeriod(); mAnimFrameTracker.setDisplayRefreshPeriod(period); + + { + std::lock_guard<std::mutex> lock(mCompositeTimingLock); + mCompositorTiming.interval = period; + } } void SurfaceFlinger::initializeDisplays() { diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 02e4a0cdab..f52bd2d5a8 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -42,6 +42,7 @@ #include <ui/PixelFormat.h> #include <ui/mat4.h> +#include <gui/FrameTimestamps.h> #include <gui/ISurfaceComposer.h> #include <gui/ISurfaceComposerClient.h> #include <gui/OccupancyTracker.h> @@ -65,7 +66,10 @@ #include "Effects/Daltonizer.h" #include <map> +#include <mutex> +#include <queue> #include <string> +#include <utility> namespace android { @@ -413,7 +417,10 @@ private: Region& dirtyRegion, Region& opaqueRegion); void preComposition(nsecs_t refreshStartTime); - void postComposition(); + void postComposition(nsecs_t refreshStartTime); + void updateCompositorTiming( + nsecs_t vsyncPhase, nsecs_t vsyncInterval, nsecs_t compositeTime, + std::shared_ptr<FenceTime>& presentFenceTime); void rebuildLayerStacks(); void setUpHWComposer(); void doComposition(); @@ -434,12 +441,13 @@ private: /* ------------------------------------------------------------------------ * VSync */ - void enableHardwareVsync(); - void resyncToHardwareVsync(bool makeAvailable); - void disableHardwareVsync(bool makeUnavailable); + void enableHardwareVsync(); + void resyncToHardwareVsync(bool makeAvailable); + void disableHardwareVsync(bool makeUnavailable); public: - void resyncWithRateLimit(); + void resyncWithRateLimit(); + void getCompositorTiming(CompositorTiming* compositorTiming); private: /* ------------------------------------------------------------------------ @@ -581,6 +589,17 @@ private: bool mPrimaryHWVsyncEnabled; bool mHWVsyncAvailable; + // protected by mCompositorTimingLock; + mutable std::mutex mCompositeTimingLock; + CompositorTiming mCompositorTiming; + + // Only accessed from the main thread. + struct CompositePresentTime { + nsecs_t composite { -1 }; + std::shared_ptr<FenceTime> display { FenceTime::NO_FENCE }; + }; + std::queue<CompositePresentTime> mCompositePresentTimes; + /* ------------------------------------------------------------------------ * Feature prototyping */ diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp index 40a27e80c0..477eb27063 100644 --- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp +++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp @@ -1036,6 +1036,11 @@ void SurfaceFlinger::onVSyncReceived(HWComposer* /*composer*/, int type, } } +void SurfaceFlinger::getCompositorTiming(CompositorTiming* compositorTiming) { + std::lock_guard<std::mutex> lock(mCompositeTimingLock); + *compositorTiming = mCompositorTiming; +} + void SurfaceFlinger::onHotplugReceived(int type, bool connected) { if (mEventThread == NULL) { // This is a temporary workaround for b/7145521. A non-null pointer @@ -1114,7 +1119,7 @@ void SurfaceFlinger::handleMessageRefresh() { setUpHWComposer(); doDebugFlashRegions(); doComposition(); - postComposition(); + postComposition(refreshStartTime); } void SurfaceFlinger::doDebugFlashRegions() @@ -1171,7 +1176,61 @@ void SurfaceFlinger::preComposition(nsecs_t refreshStartTime) } } -void SurfaceFlinger::postComposition() +void SurfaceFlinger::updateCompositorTiming( + nsecs_t vsyncPhase, nsecs_t vsyncInterval, nsecs_t compositeTime, + std::shared_ptr<FenceTime>& presentFenceTime) { + // Update queue of past composite+present times and determine the + // most recently known composite to present latency. + mCompositePresentTimes.push({compositeTime, presentFenceTime}); + nsecs_t compositeToPresentLatency = -1; + while (!mCompositePresentTimes.empty()) { + CompositePresentTime& cpt = mCompositePresentTimes.front(); + // Cached values should have been updated before calling this method, + // which helps avoid duplicate syscalls. + nsecs_t displayTime = cpt.display->getCachedSignalTime(); + if (displayTime == Fence::SIGNAL_TIME_PENDING) { + break; + } + compositeToPresentLatency = displayTime - cpt.composite; + mCompositePresentTimes.pop(); + } + + // Don't let mCompositePresentTimes grow unbounded, just in case. + while (mCompositePresentTimes.size() > 16) { + mCompositePresentTimes.pop(); + } + + // Integer division and modulo round toward 0 not -inf, so we need to + // treat negative and positive offsets differently. + nsecs_t idealLatency = (sfVsyncPhaseOffsetNs >= 0) ? + (vsyncInterval - (sfVsyncPhaseOffsetNs % vsyncInterval)) : + ((-sfVsyncPhaseOffsetNs) % vsyncInterval); + + // Snap the latency to a value that removes scheduling jitter from the + // composition and present times, which often have >1ms of jitter. + // Reducing jitter is important if an app attempts to extrapolate + // something (such as user input) to an accurate diasplay time. + // Snapping also allows an app to precisely calculate sfVsyncPhaseOffsetNs + // with (presentLatency % interval). + nsecs_t snappedCompositeToPresentLatency = -1; + if (compositeToPresentLatency >= 0) { + nsecs_t bias = vsyncInterval / 2; + int64_t extraVsyncs = + (compositeToPresentLatency - idealLatency + bias) / + vsyncInterval; + nsecs_t extraLatency = extraVsyncs * vsyncInterval; + snappedCompositeToPresentLatency = idealLatency + extraLatency; + } + + std::lock_guard<std::mutex> lock(mCompositeTimingLock); + mCompositorTiming.deadline = vsyncPhase - idealLatency; + mCompositorTiming.interval = vsyncInterval; + if (snappedCompositeToPresentLatency >= 0) { + mCompositorTiming.presentLatency = snappedCompositeToPresentLatency; + } +} + +void SurfaceFlinger::postComposition(nsecs_t refreshStartTime) { const HWComposer& hwc = getHwComposer(); const sp<const DisplayDevice> hw(getDefaultDisplayDevice()); @@ -1192,10 +1251,18 @@ void SurfaceFlinger::postComposition() mDisplayTimeline.push(retireFenceTime); mDisplayTimeline.updateSignalTimes(); + nsecs_t vsyncPhase = mPrimaryDispSync.computeNextRefresh(0); + nsecs_t vsyncInterval = mPrimaryDispSync.getPeriod(); + + // We use the refreshStartTime which might be sampled a little later than + // when we started doing work for this frame, but that should be okay + // since updateCompositorTiming has snapping logic. + updateCompositorTiming( + vsyncPhase, vsyncInterval, refreshStartTime, retireFenceTime); + mDrawingState.traverseInZOrder([&](Layer* layer) { bool frameLatched = layer->onPostComposition(glCompositionDoneFenceTime, - presentFenceTime, retireFenceTime); - + presentFenceTime, retireFenceTime, mCompositorTiming); if (frameLatched) { recordBufferingStats(layer->getName().string(), layer->getOccupancyHistory(false)); diff --git a/services/vr/vr_window_manager/hwc_callback.cpp b/services/vr/vr_window_manager/hwc_callback.cpp index 12a76d8fc2..e0c6c08715 100644 --- a/services/vr/vr_window_manager/hwc_callback.cpp +++ b/services/vr/vr_window_manager/hwc_callback.cpp @@ -12,11 +12,16 @@ namespace dvr { namespace { sp<GraphicBuffer> GetBufferFromHandle(const native_handle_t* handle) { + GraphicBufferMapper& mapper = GraphicBufferMapper::get(); + if (mapper.registerBuffer(handle) != OK) { + ALOGE("Failed to register buffer"); + return nullptr; + } + uint32_t width = 0, height = 0, stride = 0, layer_count = 1; uint64_t producer_usage = 0, consumer_usage = 0; int32_t format = 0; - GraphicBufferMapper& mapper = GraphicBufferMapper::get(); if (mapper.getDimensions(handle, &width, &height) || mapper.getStride(handle, &stride) || mapper.getFormat(handle, &format) || @@ -33,10 +38,6 @@ sp<GraphicBuffer> GetBufferFromHandle(const native_handle_t* handle) { sp<GraphicBuffer> buffer = new GraphicBuffer( width, height, format, layer_count, producer_usage, consumer_usage, stride, native_handle_clone(handle), true); - if (mapper.registerBuffer(buffer.get()) != OK) { - ALOGE("Failed to register buffer"); - return nullptr; - } return buffer; } diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp index a23056c439..32f777dc87 100644 --- a/vulkan/libvulkan/driver.cpp +++ b/vulkan/libvulkan/driver.cpp @@ -739,10 +739,8 @@ VkResult EnumerateDeviceExtensionProperties( const InstanceData& data = GetData(physicalDevice); static const std::array<VkExtensionProperties, 2> loader_extensions = {{ // WSI extensions -#if 0 // Remove this "#if 0" once the VK_KHR_incremental_present extension is ratified {VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME, VK_KHR_INCREMENTAL_PRESENT_SPEC_VERSION}, -#endif {VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME, VK_GOOGLE_DISPLAY_TIMING_SPEC_VERSION}, }}; |