Merge "Rename hw to displayDevice for consistency"
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index a217c5d..a5efff9 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -525,14 +525,10 @@
static void pokeHalServices()
{
using ::android::hidl::manager::V1_0::IServiceManager;
- using ::android::hardware::IBinder;
using ::android::hardware::hidl_string;
- using ::android::hardware::Parcel;
-
- Parcel data;
sp<IServiceManager> sm = ::android::hardware::defaultServiceManager();
- sm->list([&](const auto &interfaces) {
+ auto listRet = sm->list([&](const auto &interfaces) {
for (size_t i = 0; i < interfaces.size(); i++) {
string fqInstanceName = interfaces[i];
string::size_type n = fqInstanceName.find("/");
@@ -540,13 +536,19 @@
continue;
hidl_string fqInterfaceName = fqInstanceName.substr(0, n);
hidl_string instanceName = fqInstanceName.substr(n+1, std::string::npos);
- sm->get(fqInterfaceName, instanceName, [&](const auto &interface) {
- // TODO(b/32756130)
- // Once IServiceManager returns IBase, use interface->notifySyspropsChanged() here
- interface->transact(IBinder::SYSPROPS_TRANSACTION, data, nullptr, 0, nullptr);
+ auto getRet = sm->get(fqInterfaceName, instanceName, [&](const auto &interface) {
+ interface->notifySyspropsChanged();
});
+ if (!getRet.isOk()) {
+ fprintf(stderr, "failed to get service %s: %s\n",
+ fqInstanceName.c_str(),
+ getRet.getStatus().toString8().string());
+ }
}
});
+ if (!listRet.isOk()) {
+ fprintf(stderr, "failed to list services: %s\n", listRet.getStatus().toString8().string());
+ }
}
// Set the trace tags that userland tracing uses, and poke the running
diff --git a/include/gui/BufferQueue.h b/include/gui/BufferQueue.h
index f24f135..a523cd8 100644
--- a/include/gui/BufferQueue.h
+++ b/include/gui/BufferQueue.h
@@ -66,8 +66,9 @@
virtual void onFrameReplaced(const BufferItem& item) override;
virtual void onBuffersReleased() override;
virtual void onSidebandStreamChanged() override;
- virtual bool getFrameTimestamps(uint64_t frameNumber,
- FrameTimestamps* outTimestamps) const override;
+ virtual void addAndGetFrameTimestamps(
+ const NewFrameEventsEntry* newTimestamps,
+ FrameEventHistoryDelta* outDelta) override;
private:
// mConsumerListener is a weak reference to the IConsumerListener. This is
// the raison d'etre of ProxyConsumerListener.
diff --git a/include/gui/BufferQueueProducer.h b/include/gui/BufferQueueProducer.h
index 65dea0d..5541468 100644
--- a/include/gui/BufferQueueProducer.h
+++ b/include/gui/BufferQueueProducer.h
@@ -80,9 +80,9 @@
//
// In both cases, the producer will need to call requestBuffer to get a
// GraphicBuffer handle for the returned slot.
- virtual status_t dequeueBuffer(int *outSlot, sp<Fence>* outFence,
+ status_t dequeueBuffer(int *outSlot, sp<Fence>* outFence,
uint32_t width, uint32_t height, PixelFormat format,
- uint32_t usage);
+ uint32_t usage, FrameEventHistoryDelta* outTimestamps) override;
// See IGraphicBufferProducer::detachBuffer
virtual status_t detachBuffer(int slot);
@@ -177,8 +177,7 @@
sp<Fence>* outFence, float outTransformMatrix[16]) override;
// See IGraphicBufferProducer::getFrameTimestamps
- virtual bool getFrameTimestamps(uint64_t frameNumber,
- FrameTimestamps* outTimestamps) const override;
+ virtual void getFrameTimestamps(FrameEventHistoryDelta* outDelta) override;
// See IGraphicBufferProducer::getUniqueId
virtual status_t getUniqueId(uint64_t* outId) const override;
@@ -195,6 +194,9 @@
// BufferQueueCore::INVALID_BUFFER_SLOT otherwise
int getFreeSlotLocked() const;
+ void addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
+ FrameEventHistoryDelta* outDelta);
+
// waitForFreeSlotThenRelock finds the oldest slot in the FREE state. It may
// block if there are no available slots and we are not in non-blocking
// mode (producer and consumer controlled by the application). If it blocks,
diff --git a/include/gui/ConsumerBase.h b/include/gui/ConsumerBase.h
index 9f8b638..ce85fc3 100644
--- a/include/gui/ConsumerBase.h
+++ b/include/gui/ConsumerBase.h
@@ -180,7 +180,7 @@
// Derived classes should override this method to perform any cleanup that
// must take place when a buffer is released back to the BufferQueue. If
// it is overridden the derived class's implementation must call
- // ConsumerBase::releaseBufferLocked.e
+ // ConsumerBase::releaseBufferLocked.
virtual status_t releaseBufferLocked(int slot,
const sp<GraphicBuffer> graphicBuffer,
EGLDisplay display, EGLSyncKHR eglFence);
@@ -244,6 +244,10 @@
// if none is supplied
sp<IGraphicBufferConsumer> mConsumer;
+ // The final release fence of the most recent buffer released by
+ // releaseBufferLocked.
+ sp<Fence> mPrevFinalReleaseFence;
+
// mMutex is the mutex used to prevent concurrent access to the member
// variables of ConsumerBase objects. It must be locked whenever the
// member variables are accessed or when any of the *Locked methods are
diff --git a/include/gui/FrameTimestamps.h b/include/gui/FrameTimestamps.h
index 17352cc..0e95ec3 100644
--- a/include/gui/FrameTimestamps.h
+++ b/include/gui/FrameTimestamps.h
@@ -17,29 +17,256 @@
#ifndef ANDROID_GUI_FRAMETIMESTAMPS_H
#define ANDROID_GUI_FRAMETIMESTAMPS_H
-#include <utils/Timers.h>
+#include <ui/Fence.h>
#include <utils/Flattenable.h>
+#include <utils/StrongPointer.h>
+#include <utils/Timers.h>
+
+#include <array>
+#include <bitset>
+#include <vector>
namespace android {
-struct FrameTimestamps : public LightFlattenablePod<FrameTimestamps> {
- FrameTimestamps() :
- frameNumber(0),
- requestedPresentTime(0),
- acquireTime(0),
- refreshStartTime(0),
- glCompositionDoneTime(0),
- displayRetireTime(0),
- releaseTime(0) {}
- uint64_t frameNumber;
- nsecs_t requestedPresentTime;
- nsecs_t acquireTime;
- nsecs_t refreshStartTime;
- nsecs_t glCompositionDoneTime;
- nsecs_t displayRetireTime;
- nsecs_t releaseTime;
+struct FrameEvents;
+class FrameEventHistoryDelta;
+class String8;
+
+
+enum class FrameEvent {
+ POSTED,
+ REQUESTED_PRESENT,
+ LATCH,
+ ACQUIRE,
+ FIRST_REFRESH_START,
+ LAST_REFRESH_START,
+ GL_COMPOSITION_DONE,
+ DISPLAY_PRESENT,
+ DISPLAY_RETIRE,
+ RELEASE,
+ EVENT_COUNT, // Not an actual event.
};
+
+// A collection of timestamps corresponding to a single frame.
+struct FrameEvents {
+ bool hasPostedInfo() const;
+ bool hasRequestedPresentInfo() const;
+ bool hasLatchInfo() const;
+ bool hasFirstRefreshStartInfo() const;
+ bool hasLastRefreshStartInfo() const;
+ bool hasAcquireInfo() const;
+ bool hasGpuCompositionDoneInfo() const;
+ bool hasDisplayPresentInfo() const;
+ bool hasDisplayRetireInfo() const;
+ bool hasReleaseInfo() const;
+
+ void checkFencesForCompletion();
+ void dump(String8& outString) const;
+
+ static constexpr size_t EVENT_COUNT =
+ static_cast<size_t>(FrameEvent::EVENT_COUNT);
+ static_assert(EVENT_COUNT <= 32, "Event count sanity check failed.");
+
+ bool valid{false};
+ uint64_t frameNumber{0};
+
+ // Whether or not certain points in the frame's life cycle have been
+ // encountered help us determine if timestamps aren't available because
+ // a) we'll just never get them or b) they're not ready yet.
+ bool addPostCompositeCalled{false};
+ bool addRetireCalled{false};
+ bool addReleaseCalled{false};
+
+ nsecs_t postedTime{0};
+ nsecs_t requestedPresentTime{0};
+ nsecs_t latchTime{0};
+ nsecs_t firstRefreshStartTime{0};
+ nsecs_t lastRefreshStartTime{0};
+
+ nsecs_t acquireTime{0};
+ nsecs_t gpuCompositionDoneTime{0};
+ nsecs_t displayPresentTime{0};
+ nsecs_t displayRetireTime{0};
+ nsecs_t releaseTime{0};
+
+ sp<Fence> acquireFence{Fence::NO_FENCE};
+ sp<Fence> gpuCompositionDoneFence{Fence::NO_FENCE};
+ sp<Fence> displayPresentFence{Fence::NO_FENCE};
+ sp<Fence> displayRetireFence{Fence::NO_FENCE};
+ sp<Fence> releaseFence{Fence::NO_FENCE};
+};
+
+
+// A short history of frames that are synchronized between the consumer and
+// producer via deltas.
+class FrameEventHistory {
+public:
+ virtual ~FrameEventHistory();
+
+ FrameEvents* getFrame(uint64_t frameNumber);
+ FrameEvents* getFrame(uint64_t frameNumber, size_t* iHint);
+ void checkFencesForCompletion();
+ void dump(String8& outString) const;
+
+ static constexpr size_t MAX_FRAME_HISTORY = 8;
+
+protected:
+ std::array<FrameEvents, MAX_FRAME_HISTORY> mFrames;
+};
+
+
+// The producer's interface to FrameEventHistory
+class ProducerFrameEventHistory : public FrameEventHistory {
+public:
+ ~ProducerFrameEventHistory() override;
+
+ void updateAcquireFence(uint64_t frameNumber, sp<Fence> acquire);
+ void applyDelta(const FrameEventHistoryDelta& delta);
+
+private:
+ size_t mAcquireOffset{0};
+};
+
+
+// Used by the consumer to create a new frame event record that is
+// partially complete.
+struct NewFrameEventsEntry {
+ uint64_t frameNumber{0};
+ nsecs_t postedTime{0};
+ nsecs_t requestedPresentTime{0};
+ sp<Fence> acquireFence{Fence::NO_FENCE};
+};
+
+
+// Used by the consumer to keep track of which fields it already sent to
+// the producer.
+class FrameEventDirtyFields {
+public:
+ inline void reset() { mBitset.reset(); }
+ inline bool anyDirty() const { return mBitset.any(); }
+
+ template <FrameEvent event>
+ inline void setDirty() {
+ constexpr size_t eventIndex = static_cast<size_t>(event);
+ static_assert(eventIndex < FrameEvents::EVENT_COUNT, "Bad index.");
+ mBitset.set(eventIndex);
+ }
+
+ template <FrameEvent event>
+ inline bool isDirty() const {
+ constexpr size_t eventIndex = static_cast<size_t>(event);
+ static_assert(eventIndex < FrameEvents::EVENT_COUNT, "Bad index.");
+ return mBitset[eventIndex];
+ }
+
+private:
+ std::bitset<FrameEvents::EVENT_COUNT> mBitset;
+};
+
+
+// The consumer's interface to FrameEventHistory
+class ConsumerFrameEventHistory : public FrameEventHistory {
+public:
+ ~ConsumerFrameEventHistory() override;
+
+ 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,
+ sp<Fence> gpuCompositionDone, sp<Fence> displayPresent);
+ void addRetire(uint64_t frameNumber, sp<Fence> displayRetire);
+ void addRelease(uint64_t frameNumber, sp<Fence> release);
+
+ void getAndResetDelta(FrameEventHistoryDelta* delta);
+
+private:
+ std::array<FrameEventDirtyFields, MAX_FRAME_HISTORY> mFramesDirty;
+ size_t mQueueOffset{0};
+ size_t mCompositionOffset{0};
+ size_t mRetireOffset{0};
+ size_t mReleaseOffset{0};
+};
+
+
+// A single frame update from the consumer to producer that can be sent
+// through Binder.
+// Although this may be sent multiple times for the same frame as new
+// timestamps are set, Fences only need to be sent once.
+class FrameEventsDelta : public Flattenable<FrameEventsDelta> {
+friend class ProducerFrameEventHistory;
+public:
+ FrameEventsDelta() = default;
+ FrameEventsDelta(size_t index,
+ const FrameEvents& frameTimestamps,
+ const FrameEventDirtyFields& dirtyFields);
+
+ // Flattenable implementation
+ size_t getFlattenedSize() const;
+ size_t getFdCount() const;
+ status_t flatten(void*& buffer, size_t& size, int*& fds,
+ size_t& count) const;
+ status_t unflatten(void const*& buffer, size_t& size, int const*& fds,
+ size_t& count);
+
+private:
+ static size_t minFlattenedSize();
+
+ size_t mIndex{0};
+ uint64_t mFrameNumber{0};
+
+ bool mAddPostCompositeCalled{0};
+ bool mAddRetireCalled{0};
+ bool mAddReleaseCalled{0};
+
+ nsecs_t mPostedTime{0};
+ nsecs_t mRequestedPresentTime{0};
+ nsecs_t mLatchTime{0};
+ nsecs_t mFirstRefreshStartTime{0};
+ nsecs_t mLastRefreshStartTime{0};
+
+ sp<Fence> mGpuCompositionDoneFence{Fence::NO_FENCE};
+ sp<Fence> mDisplayPresentFence{Fence::NO_FENCE};
+ sp<Fence> mDisplayRetireFence{Fence::NO_FENCE};
+ sp<Fence> mReleaseFence{Fence::NO_FENCE};
+
+ // This is a static method with an auto return value so we can call
+ // it without needing const and non-const versions.
+ template <typename ThisT>
+ static inline auto allFences(ThisT fed) ->
+ std::array<decltype(&fed->mReleaseFence), 4> {
+ return {{
+ &fed->mGpuCompositionDoneFence, &fed->mDisplayPresentFence,
+ &fed->mDisplayRetireFence, &fed->mReleaseFence
+ }};
+ }
+};
+
+
+// A collection of updates from consumer to producer that can be sent
+// through Binder.
+class FrameEventHistoryDelta
+ : public Flattenable<FrameEventHistoryDelta> {
+
+friend class ConsumerFrameEventHistory;
+friend class ProducerFrameEventHistory;
+
+public:
+ // Flattenable implementation.
+ size_t getFlattenedSize() const;
+ size_t getFdCount() const;
+ status_t flatten(void*& buffer, size_t& size, int*& fds,
+ size_t& count) const;
+ status_t unflatten(void const*& buffer, size_t& size, int const*& fds,
+ size_t& count);
+
+private:
+ static size_t minFlattenedSize();
+
+ std::vector<FrameEventsDelta> mDeltas;
+};
+
+
} // namespace android
#endif
diff --git a/include/gui/GLConsumer.h b/include/gui/GLConsumer.h
index 6267625..6ff1303 100644
--- a/include/gui/GLConsumer.h
+++ b/include/gui/GLConsumer.h
@@ -250,7 +250,7 @@
// mEglSlots array in addition to the ConsumerBase.
virtual status_t releaseBufferLocked(int slot,
const sp<GraphicBuffer> graphicBuffer,
- EGLDisplay display, EGLSyncKHR eglFence);
+ EGLDisplay display, EGLSyncKHR eglFence) override;
status_t releaseBufferLocked(int slot,
const sp<GraphicBuffer> graphicBuffer, EGLSyncKHR eglFence) {
diff --git a/include/gui/IConsumerListener.h b/include/gui/IConsumerListener.h
index 0ab7590..93dd4ac 100644
--- a/include/gui/IConsumerListener.h
+++ b/include/gui/IConsumerListener.h
@@ -82,10 +82,11 @@
// different stream.
virtual void onSidebandStreamChanged() = 0; /* Asynchronous */
- // See IGraphicBufferProducer::getFrameTimestamps
- // This queries the consumer for the timestamps
- virtual bool getFrameTimestamps(uint64_t /*frameNumber*/,
- FrameTimestamps* /*outTimestamps*/) const { return false; }
+ // Notifies the consumer of any new producer-side timestamps and
+ // returns the combined frame history that hasn't already been retrieved.
+ virtual void addAndGetFrameTimestamps(
+ const NewFrameEventsEntry* /*newTimestamps*/,
+ FrameEventHistoryDelta* /*outDelta*/) {}
};
diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h
index 982cc9d..492db35 100644
--- a/include/gui/IGraphicBufferProducer.h
+++ b/include/gui/IGraphicBufferProducer.h
@@ -190,7 +190,8 @@
// All other negative values are an unknown error returned downstream
// from the graphics allocator (typically errno).
virtual status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w,
- uint32_t h, PixelFormat format, uint32_t usage) = 0;
+ uint32_t h, PixelFormat format, uint32_t usage,
+ FrameEventHistoryDelta* outTimestamps) = 0;
// detachBuffer attempts to remove all ownership of the buffer in the given
// slot from the buffer queue. If this call succeeds, the slot will be
@@ -295,6 +296,7 @@
struct QueueBufferInput : public Flattenable<QueueBufferInput> {
friend class Flattenable<QueueBufferInput>;
explicit inline QueueBufferInput(const Parcel& parcel);
+
// timestamp - a monotonically increasing value in nanoseconds
// isAutoTimestamp - if the timestamp was synthesized at queue time
// dataSpace - description of the contents, interpretation depends on format
@@ -305,19 +307,23 @@
// set this to Fence::NO_FENCE if the buffer is ready immediately
// sticky - the sticky transform set in Surface (only used by the LEGACY
// camera mode).
+ // getFrameTimestamps - whether or not the latest frame timestamps
+ // should be retrieved from the consumer.
inline QueueBufferInput(int64_t _timestamp, bool _isAutoTimestamp,
android_dataspace _dataSpace, const Rect& _crop,
int _scalingMode, uint32_t _transform, const sp<Fence>& _fence,
- uint32_t _sticky = 0)
+ uint32_t _sticky = 0, bool _getFrameTimestamps = false)
: timestamp(_timestamp), isAutoTimestamp(_isAutoTimestamp),
dataSpace(_dataSpace), crop(_crop), scalingMode(_scalingMode),
transform(_transform), stickyTransform(_sticky), fence(_fence),
- surfaceDamage() { }
+ surfaceDamage(), getFrameTimestamps(_getFrameTimestamps) { }
+
inline void deflate(int64_t* outTimestamp, bool* outIsAutoTimestamp,
android_dataspace* outDataSpace,
Rect* outCrop, int* outScalingMode,
uint32_t* outTransform, sp<Fence>* outFence,
- uint32_t* outStickyTransform = NULL) const {
+ uint32_t* outStickyTransform = nullptr,
+ bool* outGetFrameTimestamps = nullptr) const {
*outTimestamp = timestamp;
*outIsAutoTimestamp = bool(isAutoTimestamp);
*outDataSpace = dataSpace;
@@ -328,9 +334,13 @@
if (outStickyTransform != NULL) {
*outStickyTransform = stickyTransform;
}
+ if (outGetFrameTimestamps) {
+ *outGetFrameTimestamps = getFrameTimestamps;
+ }
}
// Flattenable protocol
+ static constexpr size_t minFlattenedSize();
size_t getFlattenedSize() const;
size_t getFdCount() const;
status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
@@ -349,41 +359,23 @@
uint32_t stickyTransform{0};
sp<Fence> fence;
Region surfaceDamage;
+ bool getFrameTimestamps{false};
};
- // QueueBufferOutput must be a POD structure
- struct QueueBufferOutput {
- // outWidth - filled with default width applied to the buffer
- // outHeight - filled with default height applied to the buffer
- // outTransformHint - filled with default transform applied to the buffer
- // outNumPendingBuffers - num buffers queued that haven't yet been acquired
- // (counting the currently queued buffer)
- inline void deflate(uint32_t* outWidth,
- uint32_t* outHeight,
- uint32_t* outTransformHint,
- uint32_t* outNumPendingBuffers,
- uint64_t* outNextFrameNumber) const {
- *outWidth = width;
- *outHeight = height;
- *outTransformHint = transformHint;
- *outNumPendingBuffers = numPendingBuffers;
- *outNextFrameNumber = nextFrameNumber;
- }
- inline void inflate(uint32_t inWidth, uint32_t inHeight,
- uint32_t inTransformHint, uint32_t inNumPendingBuffers,
- uint64_t inNextFrameNumber) {
- width = inWidth;
- height = inHeight;
- transformHint = inTransformHint;
- numPendingBuffers = inNumPendingBuffers;
- nextFrameNumber = inNextFrameNumber;
- }
- private:
+ struct QueueBufferOutput : public Flattenable<QueueBufferOutput> {
+ // Flattenable protocol
+ static constexpr size_t minFlattenedSize();
+ size_t getFlattenedSize() const;
+ size_t getFdCount() const;
+ status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
+ status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
+
uint32_t width{0};
uint32_t height{0};
uint32_t transformHint{0};
uint32_t numPendingBuffers{0};
uint64_t nextFrameNumber{0};
+ FrameEventHistoryDelta frameTimestamps;
};
virtual status_t queueBuffer(int slot, const QueueBufferInput& input,
@@ -580,13 +572,8 @@
virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
sp<Fence>* outFence, float outTransformMatrix[16]) = 0;
- // Attempts to retrieve timestamp information for the given frame number.
- // If information for the given frame number is not found, returns false.
- // Returns true otherwise.
- //
- // If a fence has not yet signaled the timestamp returned will be 0;
- virtual bool getFrameTimestamps(uint64_t /*frameNumber*/,
- FrameTimestamps* /*outTimestamps*/) const { return false; }
+ // Gets the frame events that haven't already been retrieved.
+ virtual void getFrameTimestamps(FrameEventHistoryDelta* /*outDelta*/) {}
// Returns a unique id for this BufferQueue
virtual status_t getUniqueId(uint64_t* outId) const = 0;
diff --git a/include/gui/ISurfaceComposer.h b/include/gui/ISurfaceComposer.h
index a3ee798..824e5c4 100644
--- a/include/gui/ISurfaceComposer.h
+++ b/include/gui/ISurfaceComposer.h
@@ -32,6 +32,8 @@
#include <gui/IGraphicBufferAlloc.h>
#include <gui/ISurfaceComposerClient.h>
+#include <vector>
+
namespace android {
// ----------------------------------------------------------------------------
@@ -43,6 +45,7 @@
class IDisplayEventConnection;
class IMemoryHeap;
class Rect;
+enum class FrameEvent;
/*
* This class defines the Binder IPC interface for accessing various
@@ -112,6 +115,11 @@
virtual bool authenticateSurfaceTexture(
const sp<IGraphicBufferProducer>& surface) const = 0;
+ /* Returns the frame timestamps supported by SurfaceFlinger.
+ */
+ virtual status_t getSupportedFrameTimestamps(
+ std::vector<FrameEvent>* outSupported) const = 0;
+
/* set display power mode. depending on the mode, it can either trigger
* screen on, off or low power mode and wait for it to complete.
* requires ACCESS_SURFACE_FLINGER permission.
@@ -193,6 +201,7 @@
GET_BUILT_IN_DISPLAY,
SET_TRANSACTION_STATE,
AUTHENTICATE_SURFACE,
+ GET_SUPPORTED_FRAME_TIMESTAMPS,
GET_DISPLAY_CONFIGS,
GET_ACTIVE_CONFIG,
SET_ACTIVE_CONFIG,
diff --git a/include/gui/Surface.h b/include/gui/Surface.h
index 470992c..a10dad1 100644
--- a/include/gui/Surface.h
+++ b/include/gui/Surface.h
@@ -134,11 +134,18 @@
status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
sp<Fence>* outFence, float outTransformMatrix[16]);
+ /* 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);
+
// See IGraphicBufferProducer::getFrameTimestamps
- bool getFrameTimestamps(uint64_t frameNumber,
+ status_t getFrameTimestamps(uint64_t frameNumber,
nsecs_t* outRequestedPresentTime, nsecs_t* outAcquireTime,
nsecs_t* outRefreshStartTime, nsecs_t* outGlCompositionDoneTime,
- nsecs_t* outDisplayRetireTime, nsecs_t* outReleaseTime);
+ nsecs_t* outDisplayPresentTime, nsecs_t* outDisplayRetireTime,
+ nsecs_t* outReleaseTime);
status_t getUniqueId(uint64_t* outId) const;
@@ -191,6 +198,7 @@
int dispatchSetSurfaceDamage(va_list args);
int dispatchSetSharedBufferMode(va_list args);
int dispatchSetAutoRefresh(va_list args);
+ int dispatchEnableFrameTimestamps(va_list args);
int dispatchGetFrameTimestamps(va_list args);
protected:
@@ -238,6 +246,8 @@
enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 };
private:
+ void querySupportedTimestampsLocked() const;
+
void freeAllBuffers();
int getSlotFromBufferLocked(android_native_buffer_t* buffer) const;
@@ -380,6 +390,15 @@
Condition mQueueBufferCondition;
uint64_t mNextFrameNumber;
+
+ // Mutable because ANativeWindow::query needs this class const.
+ mutable bool mQueriedSupportedTimestamps;
+ mutable bool mFrameTimestampsSupportsPresent;
+ mutable bool mFrameTimestampsSupportsRetire;
+
+ // A cached copy of the FrameEventHistory maintained by the consumer.
+ bool mEnableFrameTimestamps = false;
+ ProducerFrameEventHistory mFrameEventHistory;
};
namespace view {
diff --git a/include/ui/Fence.h b/include/ui/Fence.h
index 1df15f8..99b39d8 100644
--- a/include/ui/Fence.h
+++ b/include/ui/Fence.h
@@ -42,6 +42,11 @@
{
public:
static const sp<Fence> NO_FENCE;
+ static constexpr nsecs_t SIGNAL_TIME_PENDING = INT64_MAX;
+ static constexpr nsecs_t SIGNAL_TIME_INVALID = -1;
+ static inline bool isValidTimestamp(nsecs_t time) {
+ return time >= 0 && time < INT64_MAX;
+ }
// TIMEOUT_NEVER may be passed to the wait method to indicate that it
// should wait indefinitely for the fence to signal.
@@ -94,8 +99,8 @@
// getSignalTime returns the system monotonic clock time at which the
// fence transitioned to the signaled state. If the fence is not signaled
- // then INT64_MAX is returned. If the fence is invalid or if an error
- // occurs then -1 is returned.
+ // then SIGNAL_TIME_PENDING is returned. If the fence is invalid or if an
+ // error occurs then SIGNAL_TIME_INVALID is returned.
nsecs_t getSignalTime() const;
#if __cplusplus > 201103L
diff --git a/include/ui/FenceTime.h b/include/ui/FenceTime.h
new file mode 100644
index 0000000..27cc720
--- /dev/null
+++ b/include/ui/FenceTime.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef ANDROID_FENCE_TIME_H
+#define ANDROID_FENCE_TIME_H
+
+#include <ui/Fence.h>
+#include <utils/Flattenable.h>
+#include <utils/Timers.h>
+
+#include <atomic>
+#include <mutex>
+#include <queue>
+
+namespace android {
+
+// A wrapper around fence that only implements isValid and getSignalTime.
+// It automatically closes the fence in a thread-safe manner once the signal
+// time is known.
+class FenceTime {
+public:
+ // An atomic snapshot of the FenceTime that is flattenable.
+ //
+ // This class is needed because the FenceTime class may not stay
+ // consistent for all steps of the flattening process.
+ //
+ // Not thread safe.
+ struct Snapshot : public Flattenable<Snapshot> {
+ enum class State {
+ EMPTY,
+ FENCE,
+ SIGNAL_TIME,
+ };
+
+ Snapshot() = default; // Creates an empty snapshot.
+ explicit Snapshot(const sp<Fence>& fence);
+ explicit Snapshot(nsecs_t signalTime);
+
+ // Movable.
+ Snapshot(Snapshot&& src) = default;
+ Snapshot& operator=(Snapshot&& src) = default;
+ // Not copyable.
+ Snapshot(const Snapshot& src) = delete;
+ Snapshot& operator=(const Snapshot&& src) = delete;
+
+ // Flattenable implementation.
+ size_t getFlattenedSize() const;
+ size_t getFdCount() const;
+ status_t flatten(void*& buffer, size_t& size, int*& fds,
+ size_t& count) const;
+ status_t unflatten(void const*& buffer, size_t& size, int const*& fds,
+ size_t& count);
+
+ State state{State::EMPTY};
+ sp<Fence> fence{Fence::NO_FENCE};
+ nsecs_t signalTime{Fence::SIGNAL_TIME_INVALID};
+ };
+
+ static const std::shared_ptr<FenceTime> NO_FENCE;
+
+ explicit FenceTime(const sp<Fence>& fence);
+ explicit FenceTime(sp<Fence>&& fence);
+
+ // Passing in Fence::SIGNAL_TIME_PENDING is not allowed.
+ // Doing so will convert the signalTime to Fence::SIGNAL_TIME_INVALID.
+ explicit FenceTime(nsecs_t signalTime);
+
+ // Do not allow default construction. Share NO_FENCE or explicitly construct
+ // with Fence::SIGNAL_TIME_INVALID instead.
+ FenceTime() = delete;
+
+ // Do not allow copy, assign, or move. Use a shared_ptr to share the
+ // signalTime result. Or use getSnapshot() if a thread-safe copy is really
+ // needed.
+ FenceTime(const FenceTime&) = delete;
+ FenceTime(FenceTime&&) = delete;
+ FenceTime& operator=(const FenceTime&) = delete;
+ FenceTime& operator=(FenceTime&&) = delete;
+
+ // This method should only be called when replacing the fence with
+ // a signalTime. Since this is an indirect way of setting the signal time
+ // of a fence, the snapshot should come from a trusted source.
+ void applyTrustedSnapshot(const Snapshot& src);
+
+ bool isValid() const;
+
+ // Attempts to get the timestamp from the Fence if the timestamp isn't
+ // already cached. Otherwise, it returns the cached value.
+ nsecs_t getSignalTime();
+
+ // Gets the cached timestamp without attempting to query the Fence.
+ nsecs_t getCachedSignalTime() const;
+
+ // Returns a snapshot of the FenceTime in its current state.
+ Snapshot getSnapshot() const;
+
+ // Override new and delete since this needs 8-byte alignment, which
+ // is not guaranteed on x86.
+ static void* operator new(size_t nbytes) noexcept;
+ static void operator delete(void *p);
+
+private:
+ enum class State {
+ VALID,
+ INVALID,
+ };
+
+ const State mState{State::INVALID};
+
+ // mMutex guards mFence and mSignalTime.
+ // mSignalTime is also atomic since it is sometimes read outside the lock
+ // for quick checks.
+ mutable std::mutex mMutex;
+ sp<Fence> mFence{Fence::NO_FENCE};
+ std::atomic<nsecs_t> mSignalTime{Fence::SIGNAL_TIME_INVALID};
+};
+
+// A queue of FenceTimes that are expected to signal in FIFO order.
+// Only maintains a queue of weak pointers so it doesn't keep references
+// to Fences on its own.
+//
+// Can be used to get the signal time of a fence and close its file descriptor
+// without making a syscall for every fence later in the timeline.
+// Additionally, since the FenceTime caches the timestamp internally,
+// other timelines that reference the same FenceTime can avoid the syscall.
+//
+// FenceTimeline only keeps track of a limited number of entries to avoid
+// growing unbounded. Users of FenceTime must make sure they can work even
+// if FenceTimeline did nothing. i.e. they should eventually call
+// Fence::getSignalTime(), not only Fence::getCachedSignalTime().
+//
+// push() and updateSignalTimes() are safe to call simultaneously from
+// different threads.
+class FenceTimeline {
+public:
+ static constexpr size_t MAX_ENTRIES = 64;
+
+ void push(const std::shared_ptr<FenceTime>& fence);
+ void updateSignalTimes();
+
+private:
+ mutable std::mutex mMutex;
+ std::queue<std::weak_ptr<FenceTime>> mQueue;
+};
+
+}; // namespace android
+
+#endif // ANDROID_FENCE_TIME_H
diff --git a/include/ui/mat3.h b/include/ui/mat3.h
index cd24a44..4f5dba9 100644
--- a/include/ui/mat3.h
+++ b/include/ui/mat3.h
@@ -292,7 +292,7 @@
m_value[2] = col_type(0, 0, v.z);
}
-// construct from 16 scalars. Note that the arrangement
+// construct from 9 scalars. Note that the arrangement
// of values in the constructor is the transpose of the matrix
// notation.
template<typename T>
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 9ba85a6..bf758ce 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -71,6 +71,7 @@
"ConsumerBase.cpp",
"CpuConsumer.cpp",
"DisplayEventReceiver.cpp",
+ "FrameTimestamps.cpp",
"GLConsumer.cpp",
"GraphicBufferAlloc.cpp",
"GuiConfig.cpp",
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index 47f5eba..f76a282 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -61,13 +61,13 @@
}
}
-bool BufferQueue::ProxyConsumerListener::getFrameTimestamps(
- uint64_t frameNumber, FrameTimestamps* outTimestamps) const {
+void BufferQueue::ProxyConsumerListener::addAndGetFrameTimestamps(
+ const NewFrameEventsEntry* newTimestamps,
+ FrameEventHistoryDelta* outDelta) {
sp<ConsumerListener> listener(mConsumerListener.promote());
- if (listener != NULL) {
- return listener->getFrameTimestamps(frameNumber, outTimestamps);
+ if (listener != nullptr) {
+ listener->addAndGetFrameTimestamps(newTimestamps, outDelta);
}
- return false;
}
void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 81d4f31..75198d7 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -348,7 +348,8 @@
status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
sp<android::Fence> *outFence, uint32_t width, uint32_t height,
- PixelFormat format, uint32_t usage) {
+ PixelFormat format, uint32_t usage,
+ FrameEventHistoryDelta* outTimestamps) {
ATRACE_CALL();
{ // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
@@ -560,6 +561,8 @@
mSlots[*outSlot].mFrameNumber,
mSlots[*outSlot].mGraphicBuffer->handle, returnFlags);
+ addAndGetFrameTimestamps(nullptr, outTimestamps);
+
return returnFlags;
}
@@ -740,19 +743,21 @@
ATRACE_CALL();
ATRACE_BUFFER_INDEX(slot);
- int64_t timestamp;
+ int64_t requestedPresentTimestamp;
bool isAutoTimestamp;
android_dataspace dataSpace;
Rect crop(Rect::EMPTY_RECT);
int scalingMode;
uint32_t transform;
uint32_t stickyTransform;
- sp<Fence> fence;
- input.deflate(×tamp, &isAutoTimestamp, &dataSpace, &crop, &scalingMode,
- &transform, &fence, &stickyTransform);
+ sp<Fence> acquireFence;
+ bool getFrameTimestamps = false;
+ input.deflate(&requestedPresentTimestamp, &isAutoTimestamp, &dataSpace,
+ &crop, &scalingMode, &transform, &acquireFence, &stickyTransform,
+ &getFrameTimestamps);
Region surfaceDamage = input.getSurfaceDamage();
- if (fence == NULL) {
+ if (acquireFence == NULL) {
BQ_LOGE("queueBuffer: fence is NULL");
return BAD_VALUE;
}
@@ -771,6 +776,7 @@
sp<IConsumerListener> frameAvailableListener;
sp<IConsumerListener> frameReplacedListener;
int callbackTicket = 0;
+ uint64_t currentFrameNumber = 0;
BufferItem item;
{ // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
@@ -809,8 +815,9 @@
BQ_LOGV("queueBuffer: slot=%d/%" PRIu64 " time=%" PRIu64 " dataSpace=%d"
" crop=[%d,%d,%d,%d] transform=%#x scale=%s",
- slot, mCore->mFrameCounter + 1, timestamp, dataSpace,
- crop.left, crop.top, crop.right, crop.bottom, transform,
+ slot, mCore->mFrameCounter + 1, requestedPresentTimestamp,
+ dataSpace, crop.left, crop.top, crop.right, crop.bottom,
+ transform,
BufferItem::scalingModeName(static_cast<uint32_t>(scalingMode)));
const sp<GraphicBuffer>& graphicBuffer(mSlots[slot].mGraphicBuffer);
@@ -828,11 +835,14 @@
dataSpace = mCore->mDefaultBufferDataSpace;
}
- mSlots[slot].mFence = fence;
+ mSlots[slot].mFence = acquireFence;
mSlots[slot].mBufferState.queue();
+ // Increment the frame counter and store a local version of it
+ // for use outside the lock on mCore->mMutex.
++mCore->mFrameCounter;
- mSlots[slot].mFrameNumber = mCore->mFrameCounter;
+ currentFrameNumber = mCore->mFrameCounter;
+ mSlots[slot].mFrameNumber = currentFrameNumber;
item.mAcquireCalled = mSlots[slot].mAcquireCalled;
item.mGraphicBuffer = mSlots[slot].mGraphicBuffer;
@@ -842,12 +852,12 @@
item.mTransformToDisplayInverse =
(transform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) != 0;
item.mScalingMode = static_cast<uint32_t>(scalingMode);
- item.mTimestamp = timestamp;
+ item.mTimestamp = requestedPresentTimestamp;
item.mIsAutoTimestamp = isAutoTimestamp;
item.mDataSpace = dataSpace;
- item.mFrameNumber = mCore->mFrameCounter;
+ item.mFrameNumber = currentFrameNumber;
item.mSlot = slot;
- item.mFence = fence;
+ item.mFence = acquireFence;
item.mIsDroppable = mCore->mAsyncMode ||
mCore->mDequeueBufferCannotBlock ||
(mCore->mSharedBufferMode && mCore->mSharedBufferSlot == slot);
@@ -908,10 +918,11 @@
mCore->mDequeueCondition.broadcast();
mCore->mLastQueuedSlot = slot;
- output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight,
- mCore->mTransformHint,
- static_cast<uint32_t>(mCore->mQueue.size()),
- mCore->mFrameCounter + 1);
+ output->width = mCore->mDefaultWidth;
+ output->height = mCore->mDefaultHeight;
+ output->transformHint = mCore->mTransformHint;
+ output->numPendingBuffers = static_cast<uint32_t>(mCore->mQueue.size());
+ output->nextFrameNumber = mCore->mFrameCounter + 1;
ATRACE_INT(mCore->mConsumerName.string(),
static_cast<int32_t>(mCore->mQueue.size()));
@@ -958,10 +969,21 @@
// small trade-off in favor of latency rather than throughput.
mLastQueueBufferFence->waitForever("Throttling EGL Production");
}
- mLastQueueBufferFence = fence;
+ mLastQueueBufferFence = acquireFence;
mLastQueuedCrop = item.mCrop;
mLastQueuedTransform = item.mTransform;
+ // Update and get FrameEventHistory.
+ nsecs_t postedTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ NewFrameEventsEntry newFrameEventsEntry = {
+ currentFrameNumber,
+ postedTime,
+ requestedPresentTimestamp,
+ acquireFence
+ };
+ addAndGetFrameTimestamps(&newFrameEventsEntry,
+ getFrameTimestamps ? &output->frameTimestamps : nullptr);
+
return NO_ERROR;
}
@@ -1126,10 +1148,13 @@
case NATIVE_WINDOW_API_MEDIA:
case NATIVE_WINDOW_API_CAMERA:
mCore->mConnectedApi = api;
- output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight,
- mCore->mTransformHint,
- static_cast<uint32_t>(mCore->mQueue.size()),
- mCore->mFrameCounter + 1);
+
+ output->width = mCore->mDefaultWidth;
+ output->height = mCore->mDefaultHeight;
+ output->transformHint = mCore->mTransformHint;
+ output->numPendingBuffers =
+ static_cast<uint32_t>(mCore->mQueue.size());
+ output->nextFrameNumber = mCore->mFrameCounter + 1;
if (listener != NULL) {
// Set up a death notification so that we can disconnect
@@ -1449,20 +1474,27 @@
return NO_ERROR;
}
-bool BufferQueueProducer::getFrameTimestamps(uint64_t frameNumber,
- FrameTimestamps* outTimestamps) const {
- ATRACE_CALL();
- BQ_LOGV("getFrameTimestamps, %" PRIu64, frameNumber);
- sp<IConsumerListener> listener;
+void BufferQueueProducer::getFrameTimestamps(FrameEventHistoryDelta* outDelta) {
+ addAndGetFrameTimestamps(nullptr, outDelta);
+}
+void BufferQueueProducer::addAndGetFrameTimestamps(
+ const NewFrameEventsEntry* newTimestamps,
+ FrameEventHistoryDelta* outDelta) {
+ if (newTimestamps == nullptr && outDelta == nullptr) {
+ return;
+ }
+
+ ATRACE_CALL();
+ BQ_LOGV("addAndGetFrameTimestamps");
+ sp<IConsumerListener> listener;
{
Mutex::Autolock lock(mCore->mMutex);
listener = mCore->mConsumerListener;
}
if (listener != NULL) {
- return listener->getFrameTimestamps(frameNumber, outTimestamps);
+ listener->addAndGetFrameTimestamps(newTimestamps, outDelta);
}
- return false;
}
void BufferQueueProducer::binderDied(const wp<android::IBinder>& /* who */) {
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index 3cf3078..be2b1af 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -56,7 +56,8 @@
ConsumerBase::ConsumerBase(const sp<IGraphicBufferConsumer>& bufferQueue, bool controlledByApp) :
mAbandoned(false),
- mConsumer(bufferQueue) {
+ mConsumer(bufferQueue),
+ mPrevFinalReleaseFence(Fence::NO_FENCE) {
// Choose a name using the PID and a process-unique ID.
mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
@@ -366,6 +367,7 @@
freeBufferLocked(slot);
}
+ mPrevFinalReleaseFence = mSlots[slot].mFence;
mSlots[slot].mFence = Fence::NO_FENCE;
return err;
diff --git a/libs/gui/FrameTimestamps.cpp b/libs/gui/FrameTimestamps.cpp
new file mode 100644
index 0000000..30ff65f
--- /dev/null
+++ b/libs/gui/FrameTimestamps.cpp
@@ -0,0 +1,648 @@
+/*
+* Copyright 2016 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 <gui/FrameTimestamps.h>
+
+#include <inttypes.h>
+#include <utils/String8.h>
+
+#include <algorithm>
+#include <limits>
+#include <numeric>
+
+namespace android {
+
+static inline bool isValidTimestamp(nsecs_t time) {
+ return time > 0 && time < INT64_MAX;
+}
+
+// ============================================================================
+// FrameEvents
+// ============================================================================
+
+bool FrameEvents::hasPostedInfo() const {
+ return isValidTimestamp(postedTime);
+}
+
+bool FrameEvents::hasRequestedPresentInfo() const {
+ return isValidTimestamp(requestedPresentTime);
+}
+
+bool FrameEvents::hasLatchInfo() const {
+ return isValidTimestamp(latchTime);
+}
+
+bool FrameEvents::hasFirstRefreshStartInfo() const {
+ return isValidTimestamp(firstRefreshStartTime);
+}
+
+bool FrameEvents::hasLastRefreshStartInfo() const {
+ // The last refresh start time may continue to update until a new frame
+ // is latched. We know we have the final value once the release or retire
+ // info is set. See ConsumerFrameEventHistory::addRetire/Release.
+ return addRetireCalled || addReleaseCalled;
+}
+
+bool FrameEvents::hasAcquireInfo() const {
+ return isValidTimestamp(acquireTime) || acquireFence->isValid();
+}
+
+bool FrameEvents::hasGpuCompositionDoneInfo() const {
+ // We may not get a gpuCompositionDone in addPostComposite if
+ // client/gles compositing isn't needed.
+ return addPostCompositeCalled;
+}
+
+bool FrameEvents::hasDisplayPresentInfo() const {
+ // We may not get a displayPresent in addPostComposite for HWC1.
+ return addPostCompositeCalled;
+}
+
+bool FrameEvents::hasDisplayRetireInfo() const {
+ // We may not get a displayRetire in addRetire for HWC2.
+ return addRetireCalled;
+}
+
+bool FrameEvents::hasReleaseInfo() const {
+ return addReleaseCalled;
+}
+
+static void checkFenceForCompletion(sp<Fence>* fence, nsecs_t* dstTime) {
+ if ((*fence)->isValid()) {
+ nsecs_t time = (*fence)->getSignalTime();
+ if (isValidTimestamp(time)) {
+ *dstTime = time;
+ *fence = Fence::NO_FENCE;
+ }
+ }
+}
+
+void FrameEvents::checkFencesForCompletion() {
+ checkFenceForCompletion(&acquireFence, &acquireTime);
+ checkFenceForCompletion(&gpuCompositionDoneFence, &gpuCompositionDoneTime);
+ checkFenceForCompletion(&displayPresentFence, &displayPresentTime);
+ checkFenceForCompletion(&displayRetireFence, &displayRetireTime);
+ checkFenceForCompletion(&releaseFence, &releaseTime);
+}
+
+void FrameEvents::dump(String8& outString) const
+{
+ if (!valid) {
+ return;
+ }
+
+ outString.appendFormat("-- Frame %" PRIu64 "\n", frameNumber);
+ outString.appendFormat("--- Posted \t%" PRId64 "\n", postedTime);
+ outString.appendFormat("--- Req. Present\t%" PRId64 "\n", requestedPresentTime);
+
+ outString.appendFormat("--- Latched \t");
+ if (isValidTimestamp(latchTime)) {
+ outString.appendFormat("%" PRId64 "\n", latchTime);
+ } else {
+ outString.appendFormat("Pending\n");
+ }
+
+ outString.appendFormat("--- Refresh (First)\t");
+ if (isValidTimestamp(firstRefreshStartTime)) {
+ outString.appendFormat("%" PRId64 "\n", firstRefreshStartTime);
+ } else {
+ outString.appendFormat("Pending\n");
+ }
+
+ outString.appendFormat("--- Refresh (Last)\t");
+ if (isValidTimestamp(lastRefreshStartTime)) {
+ outString.appendFormat("%" PRId64 "\n", lastRefreshStartTime);
+ } else {
+ outString.appendFormat("Pending\n");
+ }
+
+ outString.appendFormat("--- Acquire \t");
+ if (isValidTimestamp(acquireTime)) {
+ outString.appendFormat("%" PRId64 "\n", acquireTime);
+ } else {
+ outString.appendFormat("Pending\n");
+ }
+
+ outString.appendFormat("--- GPU Composite Done\t");
+ if (isValidTimestamp(gpuCompositionDoneTime)) {
+ outString.appendFormat("%" PRId64 "\n", gpuCompositionDoneTime);
+ } else if (!addPostCompositeCalled || gpuCompositionDoneFence->isValid()) {
+ outString.appendFormat("Pending\n");
+ } else {
+ outString.appendFormat("N/A\n");
+ }
+
+ outString.appendFormat("--- Display Present\t");
+ if (isValidTimestamp(displayPresentTime)) {
+ outString.appendFormat("%" PRId64 "\n", displayPresentTime);
+ } else if (!addPostCompositeCalled || displayPresentFence->isValid()) {
+ outString.appendFormat("Pending\n");
+ } else {
+ outString.appendFormat("N/A\n");
+ }
+
+ outString.appendFormat("--- Display Retire\t");
+ if (isValidTimestamp(displayRetireTime)) {
+ outString.appendFormat("%" PRId64 "\n", displayRetireTime);
+ } else if (!addRetireCalled || displayRetireFence->isValid()) {
+ outString.appendFormat("Pending\n");
+ } else {
+ outString.appendFormat("N/A\n");
+ }
+
+ outString.appendFormat("--- Release \t");
+ if (isValidTimestamp(releaseTime)) {
+ outString.appendFormat("%" PRId64 "\n", releaseTime);
+ } else {
+ outString.appendFormat("Pending\n");
+ }
+}
+
+
+// ============================================================================
+// FrameEventHistory
+// ============================================================================
+
+namespace {
+
+struct FrameNumberEqual {
+ FrameNumberEqual(uint64_t frameNumber) : mFrameNumber(frameNumber) {}
+ bool operator()(const FrameEvents& frame) {
+ return frame.valid && mFrameNumber == frame.frameNumber;
+ }
+ const uint64_t mFrameNumber;
+};
+
+} // namespace
+
+FrameEventHistory::~FrameEventHistory() = default;
+
+FrameEvents* FrameEventHistory::getFrame(uint64_t frameNumber) {
+ auto frame = std::find_if(
+ mFrames.begin(), mFrames.end(), FrameNumberEqual(frameNumber));
+ return frame == mFrames.end() ? nullptr : &(*frame);
+}
+
+FrameEvents* FrameEventHistory::getFrame(uint64_t frameNumber, size_t* iHint) {
+ *iHint = std::min(*iHint, mFrames.size());
+ auto hint = mFrames.begin() + *iHint;
+ auto frame = std::find_if(
+ hint, mFrames.end(), FrameNumberEqual(frameNumber));
+ if (frame == mFrames.end()) {
+ frame = std::find_if(
+ mFrames.begin(), hint, FrameNumberEqual(frameNumber));
+ if (frame == hint) {
+ return nullptr;
+ }
+ }
+ *iHint = static_cast<size_t>(std::distance(mFrames.begin(), frame));
+ return &(*frame);
+}
+
+void FrameEventHistory::checkFencesForCompletion() {
+ for (auto& frame : mFrames) {
+ frame.checkFencesForCompletion();
+ }
+}
+
+// Uses !|valid| as the MSB.
+static bool FrameNumberLessThan(
+ const FrameEvents& lhs, const FrameEvents& rhs) {
+ if (lhs.valid == rhs.valid) {
+ return lhs.frameNumber < rhs.frameNumber;
+ }
+ return lhs.valid;
+}
+
+void FrameEventHistory::dump(String8& outString) const {
+ auto earliestFrame = std::min_element(
+ mFrames.begin(), mFrames.end(), &FrameNumberLessThan);
+ if (!earliestFrame->valid) {
+ outString.appendFormat("-- N/A\n");
+ return;
+ }
+ for (auto frame = earliestFrame; frame != mFrames.end(); ++frame) {
+ frame->dump(outString);
+ }
+ for (auto frame = mFrames.begin(); frame != earliestFrame; ++frame) {
+ frame->dump(outString);
+ }
+}
+
+
+// ============================================================================
+// ProducerFrameEventHistory
+// ============================================================================
+
+ProducerFrameEventHistory::~ProducerFrameEventHistory() = default;
+
+void ProducerFrameEventHistory::updateAcquireFence(
+ uint64_t frameNumber, sp<Fence> acquire) {
+ FrameEvents* frame = getFrame(frameNumber, &mAcquireOffset);
+ if (frame == nullptr) {
+ ALOGE("ProducerFrameEventHistory::updateAcquireFence: "
+ "Did not find frame.");
+ return;
+ }
+
+ if (acquire->isValid()) {
+ frame->acquireFence = acquire;
+ } else {
+ // If there isn't an acquire fence, assume that buffer was
+ // ready for the consumer when posted.
+ frame->acquireTime = frame->postedTime;
+ }
+}
+
+static void applyFenceDelta(sp<Fence>* dst, const sp<Fence>& src) {
+ if (src->isValid()) {
+ if ((*dst)->isValid()) {
+ ALOGE("applyFenceDelta: Unexpected fence.");
+ }
+ *dst = src;
+ }
+}
+
+void ProducerFrameEventHistory::applyDelta(
+ const FrameEventHistoryDelta& delta) {
+ for (auto& d : delta.mDeltas) {
+ // Avoid out-of-bounds access.
+ if (d.mIndex >= mFrames.size()) {
+ ALOGE("ProducerFrameEventHistory::applyDelta: Bad index.");
+ return;
+ }
+
+ FrameEvents& frame = mFrames[d.mIndex];
+
+ frame.addPostCompositeCalled = d.mAddPostCompositeCalled != 0;
+ frame.addRetireCalled = d.mAddRetireCalled != 0;
+ frame.addReleaseCalled = d.mAddReleaseCalled != 0;
+
+ frame.postedTime = d.mPostedTime;
+ frame.requestedPresentTime = d.mRequestedPresentTime;
+ frame.latchTime = d.mLatchTime;
+ frame.firstRefreshStartTime = d.mFirstRefreshStartTime;
+ frame.lastRefreshStartTime = d.mLastRefreshStartTime;
+
+ if (frame.frameNumber == d.mFrameNumber) {
+ // Existing frame. Merge.
+ // Consumer never sends timestamps of fences, only the fences
+ // themselves, so we never need to update the fence timestamps here.
+ applyFenceDelta(
+ &frame.gpuCompositionDoneFence, d.mGpuCompositionDoneFence);
+ applyFenceDelta(&frame.displayPresentFence, d.mDisplayPresentFence);
+ applyFenceDelta(&frame.displayRetireFence, d.mDisplayRetireFence);
+ applyFenceDelta(&frame.releaseFence, d.mReleaseFence);
+ } else {
+ // New frame. Overwrite.
+ frame.frameNumber = d.mFrameNumber;
+
+ frame.gpuCompositionDoneFence = d.mGpuCompositionDoneFence;
+ frame.displayPresentFence = d.mDisplayPresentFence;
+ frame.displayRetireFence = d.mDisplayRetireFence;
+ frame.releaseFence = d.mReleaseFence;
+
+ // Set aquire fence and time at this point.
+ frame.acquireTime = 0;
+ frame.acquireFence = Fence::NO_FENCE;
+
+ // Reset fence-related timestamps
+ frame.gpuCompositionDoneTime = 0;
+ frame.displayPresentTime = 0;
+ frame.displayRetireTime = 0;
+ frame.releaseTime = 0;
+
+ // The consumer only sends valid frames.
+ frame.valid = true;
+ }
+ }
+}
+
+
+// ============================================================================
+// ConsumerFrameEventHistory
+// ============================================================================
+
+ConsumerFrameEventHistory::~ConsumerFrameEventHistory() = default;
+
+void ConsumerFrameEventHistory::addQueue(const NewFrameEventsEntry& newEntry) {
+ // Overwrite all fields of the frame with default values unless set here.
+ FrameEvents newTimestamps;
+ newTimestamps.frameNumber = newEntry.frameNumber;
+ newTimestamps.postedTime = newEntry.postedTime;
+ newTimestamps.requestedPresentTime = newEntry.requestedPresentTime;
+ newTimestamps.acquireFence = newEntry.acquireFence;
+ newTimestamps.valid = true;
+ mFrames[mQueueOffset] = newTimestamps;
+
+ // Note: We avoid sending the acquire fence back to the caller since
+ // they have the original one already, so there is no need to set the
+ // acquire dirty bit.
+ mFramesDirty[mQueueOffset].setDirty<FrameEvent::POSTED>();
+
+ mQueueOffset = (mQueueOffset + 1) % mFrames.size();
+}
+
+void ConsumerFrameEventHistory::addLatch(
+ uint64_t frameNumber, nsecs_t latchTime) {
+ FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset);
+ if (frame == nullptr) {
+ ALOGE("ConsumerFrameEventHistory::addLatch: Did not find frame.");
+ return;
+ }
+ frame->latchTime = latchTime;
+ mFramesDirty[mCompositionOffset].setDirty<FrameEvent::LATCH>();
+}
+
+void ConsumerFrameEventHistory::addPreComposition(
+ uint64_t frameNumber, nsecs_t refreshStartTime) {
+ FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset);
+ if (frame == nullptr) {
+ ALOGE("ConsumerFrameEventHistory::addPreComposition: "
+ "Did not find frame.");
+ return;
+ }
+ frame->lastRefreshStartTime = refreshStartTime;
+ mFramesDirty[mCompositionOffset].setDirty<FrameEvent::LAST_REFRESH_START>();
+ if (!isValidTimestamp(frame->firstRefreshStartTime)) {
+ frame->firstRefreshStartTime = refreshStartTime;
+ mFramesDirty[mCompositionOffset].setDirty<FrameEvent::FIRST_REFRESH_START>();
+ }
+}
+
+void ConsumerFrameEventHistory::addPostComposition(uint64_t frameNumber,
+ sp<Fence> gpuCompositionDone, sp<Fence> displayPresent) {
+ FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset);
+ if (frame == nullptr) {
+ ALOGE("ConsumerFrameEventHistory::addPostComposition: "
+ "Did not find frame.");
+ return;
+ }
+ // Only get GPU and present info for the first composite.
+ if (!frame->addPostCompositeCalled) {
+ frame->addPostCompositeCalled = true;
+ frame->gpuCompositionDoneFence = gpuCompositionDone;
+ mFramesDirty[mCompositionOffset].setDirty<FrameEvent::GL_COMPOSITION_DONE>();
+ if (!frame->displayPresentFence->isValid()) {
+ frame->displayPresentFence = displayPresent;
+ mFramesDirty[mCompositionOffset].setDirty<FrameEvent::DISPLAY_PRESENT>();
+ }
+ }
+}
+
+void ConsumerFrameEventHistory::addRetire(
+ uint64_t frameNumber, sp<Fence> displayRetire) {
+ FrameEvents* frame = getFrame(frameNumber, &mRetireOffset);
+ if (frame == nullptr) {
+ ALOGE("ConsumerFrameEventHistory::addRetire: Did not find frame.");
+ return;
+ }
+ frame->addRetireCalled = true;
+ frame->displayRetireFence = displayRetire;
+ mFramesDirty[mRetireOffset].setDirty<FrameEvent::DISPLAY_RETIRE>();
+}
+
+void ConsumerFrameEventHistory::addRelease(
+ uint64_t frameNumber, sp<Fence> release) {
+ FrameEvents* frame = getFrame(frameNumber, &mReleaseOffset);
+ if (frame == nullptr) {
+ ALOGE("ConsumerFrameEventHistory::addRelease: Did not find frame.");
+ return;
+ }
+ frame->addReleaseCalled = true;
+ frame->releaseFence = release;
+ mFramesDirty[mReleaseOffset].setDirty<FrameEvent::RELEASE>();
+}
+
+void ConsumerFrameEventHistory::getAndResetDelta(
+ FrameEventHistoryDelta* delta) {
+ delta->mDeltas.reserve(mFramesDirty.size());
+ for (size_t i = 0; i < mFramesDirty.size(); i++) {
+ if (mFramesDirty[i].anyDirty()) {
+ delta->mDeltas.push_back(
+ FrameEventsDelta(i, mFrames[i], mFramesDirty[i]));
+ mFramesDirty[i].reset();
+ }
+ }
+}
+
+
+// ============================================================================
+// FrameEventsDelta
+// ============================================================================
+
+FrameEventsDelta::FrameEventsDelta(
+ size_t index,
+ const FrameEvents& frameTimestamps,
+ const FrameEventDirtyFields& dirtyFields)
+ : mIndex(index),
+ mFrameNumber(frameTimestamps.frameNumber),
+ mAddPostCompositeCalled(frameTimestamps.addPostCompositeCalled),
+ mAddRetireCalled(frameTimestamps.addRetireCalled),
+ mAddReleaseCalled(frameTimestamps.addReleaseCalled),
+ mPostedTime(frameTimestamps.postedTime),
+ mRequestedPresentTime(frameTimestamps.requestedPresentTime),
+ mLatchTime(frameTimestamps.latchTime),
+ mFirstRefreshStartTime(frameTimestamps.firstRefreshStartTime),
+ mLastRefreshStartTime(frameTimestamps.lastRefreshStartTime) {
+ mGpuCompositionDoneFence =
+ dirtyFields.isDirty<FrameEvent::GL_COMPOSITION_DONE>() ?
+ frameTimestamps.gpuCompositionDoneFence : Fence::NO_FENCE;
+ mDisplayPresentFence = dirtyFields.isDirty<FrameEvent::DISPLAY_PRESENT>() ?
+ frameTimestamps.displayPresentFence : Fence::NO_FENCE;
+ mDisplayRetireFence = dirtyFields.isDirty<FrameEvent::DISPLAY_RETIRE>() ?
+ frameTimestamps.displayRetireFence : Fence::NO_FENCE;
+ mReleaseFence = dirtyFields.isDirty<FrameEvent::RELEASE>() ?
+ frameTimestamps.releaseFence : Fence::NO_FENCE;
+}
+
+size_t FrameEventsDelta::minFlattenedSize() {
+ constexpr size_t min =
+ sizeof(FrameEventsDelta::mFrameNumber) +
+ sizeof(uint8_t) + // mIndex
+ sizeof(uint8_t) + // mAddPostCompositeCalled
+ sizeof(uint8_t) + // mAddRetireCalled
+ sizeof(uint8_t) + // mAddReleaseCalled
+ sizeof(FrameEventsDelta::mPostedTime) +
+ sizeof(FrameEventsDelta::mRequestedPresentTime) +
+ sizeof(FrameEventsDelta::mLatchTime) +
+ sizeof(FrameEventsDelta::mFirstRefreshStartTime) +
+ sizeof(FrameEventsDelta::mLastRefreshStartTime);
+ return min;
+}
+
+// Flattenable implementation
+size_t FrameEventsDelta::getFlattenedSize() const {
+ auto fences = allFences(this);
+ return minFlattenedSize() +
+ std::accumulate(fences.begin(), fences.end(), size_t(0),
+ [](size_t a, const sp<Fence>* fence) {
+ return a + (*fence)->getFlattenedSize();
+ });
+}
+
+size_t FrameEventsDelta::getFdCount() const {
+ auto fences = allFences(this);
+ return std::accumulate(fences.begin(), fences.end(), size_t(0),
+ [](size_t a, const sp<Fence>* fence) {
+ return a + (*fence)->getFdCount();
+ });
+}
+
+status_t FrameEventsDelta::flatten(void*& buffer, size_t& size, int*& fds,
+ size_t& count) const {
+ if (size < getFlattenedSize() || count < getFdCount()) {
+ return NO_MEMORY;
+ }
+
+ if (mIndex >= FrameEventHistory::MAX_FRAME_HISTORY ||
+ mIndex > std::numeric_limits<uint8_t>::max()) {
+ return BAD_VALUE;
+ }
+
+ FlattenableUtils::write(buffer, size, mFrameNumber);
+
+ // These are static_cast to uint8_t for alignment.
+ FlattenableUtils::write(buffer, size, static_cast<uint8_t>(mIndex));
+ FlattenableUtils::write(
+ buffer, size, static_cast<uint8_t>(mAddPostCompositeCalled));
+ FlattenableUtils::write(
+ buffer, size, static_cast<uint8_t>(mAddRetireCalled));
+ FlattenableUtils::write(
+ buffer, size, static_cast<uint8_t>(mAddReleaseCalled));
+
+ FlattenableUtils::write(buffer, size, mPostedTime);
+ FlattenableUtils::write(buffer, size, mRequestedPresentTime);
+ FlattenableUtils::write(buffer, size, mLatchTime);
+ FlattenableUtils::write(buffer, size, mFirstRefreshStartTime);
+ FlattenableUtils::write(buffer, size, mLastRefreshStartTime);
+
+ // Fences
+ for (auto fence : allFences(this)) {
+ status_t status = (*fence)->flatten(buffer, size, fds, count);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+status_t FrameEventsDelta::unflatten(void const*& buffer, size_t& size,
+ int const*& fds, size_t& count) {
+ if (size < minFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, mFrameNumber);
+
+ // These were written as uint8_t for alignment.
+ uint8_t temp = 0;
+ FlattenableUtils::read(buffer, size, temp);
+ mIndex = temp;
+ if (mIndex >= FrameEventHistory::MAX_FRAME_HISTORY) {
+ return BAD_VALUE;
+ }
+ FlattenableUtils::read(buffer, size, temp);
+ mAddPostCompositeCalled = static_cast<bool>(temp);
+ FlattenableUtils::read(buffer, size, temp);
+ mAddRetireCalled = static_cast<bool>(temp);
+ FlattenableUtils::read(buffer, size, temp);
+ mAddReleaseCalled = static_cast<bool>(temp);
+
+ FlattenableUtils::read(buffer, size, mPostedTime);
+ FlattenableUtils::read(buffer, size, mRequestedPresentTime);
+ FlattenableUtils::read(buffer, size, mLatchTime);
+ FlattenableUtils::read(buffer, size, mFirstRefreshStartTime);
+ FlattenableUtils::read(buffer, size, mLastRefreshStartTime);
+
+ // Fences
+ for (auto fence : allFences(this)) {
+ *fence = new Fence;
+ status_t status = (*fence)->unflatten(buffer, size, fds, count);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+
+// ============================================================================
+// FrameEventHistoryDelta
+// ============================================================================
+
+size_t FrameEventHistoryDelta::minFlattenedSize() {
+ return sizeof(uint32_t);
+}
+
+size_t FrameEventHistoryDelta::getFlattenedSize() const {
+ return minFlattenedSize() +
+ std::accumulate(mDeltas.begin(), mDeltas.end(), size_t(0),
+ [](size_t a, const FrameEventsDelta& delta) {
+ return a + delta.getFlattenedSize();
+ });
+}
+
+size_t FrameEventHistoryDelta::getFdCount() const {
+ return std::accumulate(mDeltas.begin(), mDeltas.end(), size_t(0),
+ [](size_t a, const FrameEventsDelta& delta) {
+ return a + delta.getFdCount();
+ });
+}
+
+status_t FrameEventHistoryDelta::flatten(
+ void*& buffer, size_t& size, int*& fds, size_t& count) const {
+ if (mDeltas.size() > FrameEventHistory::MAX_FRAME_HISTORY) {
+ return BAD_VALUE;
+ }
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(
+ buffer, size, static_cast<uint32_t>(mDeltas.size()));
+ for (auto& d : mDeltas) {
+ status_t status = d.flatten(buffer, size, fds, count);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+status_t FrameEventHistoryDelta::unflatten(
+ void const*& buffer, size_t& size, int const*& fds, size_t& count) {
+ if (size < minFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ uint32_t deltaCount = 0;
+ FlattenableUtils::read(buffer, size, deltaCount);
+ if (deltaCount > FrameEventHistory::MAX_FRAME_HISTORY) {
+ return BAD_VALUE;
+ }
+ mDeltas.resize(deltaCount);
+ for (auto& d : mDeltas) {
+ status_t status = d.unflatten(buffer, size, fds, count);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+
+} // namespace android
diff --git a/libs/gui/IConsumerListener.cpp b/libs/gui/IConsumerListener.cpp
index ff7b83a..3d893b1 100644
--- a/libs/gui/IConsumerListener.cpp
+++ b/libs/gui/IConsumerListener.cpp
@@ -61,42 +61,6 @@
data.writeInterfaceToken(IConsumerListener::getInterfaceDescriptor());
remote()->transact(ON_SIDEBAND_STREAM_CHANGED, data, &reply, IBinder::FLAG_ONEWAY);
}
-
- virtual bool getFrameTimestamps(uint64_t frameNumber,
- FrameTimestamps* outTimestamps) const {
- Parcel data, reply;
- status_t result = data.writeInterfaceToken(
- IConsumerListener::getInterfaceDescriptor());
- if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to write token: %d", result);
- return false;
- }
- result = data.writeUint64(frameNumber);
- if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to write: %d", result);
- return false;
- }
- result = remote()->transact(GET_FRAME_TIMESTAMPS, data, &reply);
- if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to transact: %d", result);
- return false;
- }
- bool found = false;
- result = reply.readBool(&found);
- if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to read: %d", result);
- return false;
- }
- if (found) {
- result = reply.read(*outTimestamps);
- if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to read timestamps: %d",
- result);
- return false;
- }
- }
- return found;
- }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -125,30 +89,6 @@
CHECK_INTERFACE(IConsumerListener, data, reply);
onSidebandStreamChanged();
return NO_ERROR; }
- case GET_FRAME_TIMESTAMPS: {
- CHECK_INTERFACE(IConsumerListener, data, reply);
- uint64_t frameNumber = 0;
- status_t result = data.readUint64(&frameNumber);
- if (result != NO_ERROR) {
- ALOGE("onTransact failed to read: %d", result);
- return result;
- }
- FrameTimestamps timestamps;
- bool found = getFrameTimestamps(frameNumber, ×tamps);
- result = reply->writeBool(found);
- if (result != NO_ERROR) {
- ALOGE("onTransact failed to write: %d", result);
- return result;
- }
- if (found) {
- result = reply->write(timestamps);
- if (result != NO_ERROR) {
- ALOGE("onTransact failed to write timestamps: %d", result);
- return result;
- }
- }
- return NO_ERROR;
- }
}
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 846c205..e37b65b 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -118,24 +118,35 @@
}
virtual status_t dequeueBuffer(int *buf, sp<Fence>* fence, uint32_t width,
- uint32_t height, PixelFormat format, uint32_t usage) {
+ uint32_t height, PixelFormat format, uint32_t usage,
+ FrameEventHistoryDelta* outTimestamps) {
Parcel data, reply;
+ bool getFrameTimestamps = (outTimestamps != nullptr);
+
data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
data.writeUint32(width);
data.writeUint32(height);
data.writeInt32(static_cast<int32_t>(format));
data.writeUint32(usage);
+ data.writeBool(getFrameTimestamps);
+
status_t result = remote()->transact(DEQUEUE_BUFFER, data, &reply);
if (result != NO_ERROR) {
return result;
}
+
*buf = reply.readInt32();
- bool nonNull = reply.readInt32();
- if (nonNull) {
- *fence = new Fence();
- result = reply.read(**fence);
+ *fence = new Fence();
+ result = reply.read(**fence);
+ if (result != NO_ERROR) {
+ fence->clear();
+ return result;
+ }
+ if (getFrameTimestamps) {
+ result = reply.read(*outTimestamps);
if (result != NO_ERROR) {
- fence->clear();
+ ALOGE("IGBP::dequeueBuffer failed to read timestamps: %d",
+ result);
return result;
}
}
@@ -211,14 +222,21 @@
virtual status_t queueBuffer(int buf,
const QueueBufferInput& input, QueueBufferOutput* output) {
Parcel data, reply;
+
data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
data.writeInt32(buf);
data.write(input);
+
status_t result = remote()->transact(QUEUE_BUFFER, data, &reply);
if (result != NO_ERROR) {
return result;
}
- memcpy(output, reply.readInplace(sizeof(*output)), sizeof(*output));
+
+ result = reply.read(*output);
+ if (result != NO_ERROR) {
+ return result;
+ }
+
result = reply.readInt32();
return result;
}
@@ -265,7 +283,7 @@
if (result != NO_ERROR) {
return result;
}
- memcpy(output, reply.readInplace(sizeof(*output)), sizeof(*output));
+ reply.read(*output);
result = reply.readInt32();
return result;
}
@@ -422,40 +440,24 @@
return result;
}
- virtual bool getFrameTimestamps(uint64_t frameNumber,
- FrameTimestamps* outTimestamps) const {
+ virtual void getFrameTimestamps(FrameEventHistoryDelta* outDelta) {
Parcel data, reply;
status_t result = data.writeInterfaceToken(
IGraphicBufferProducer::getInterfaceDescriptor());
if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to write token: %d", result);
- return false;
- }
- result = data.writeUint64(frameNumber);
- if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to write: %d", result);
- return false;
+ ALOGE("IGBP::getFrameTimestamps failed to write token: %d", result);
+ return;
}
result = remote()->transact(GET_FRAME_TIMESTAMPS, data, &reply);
if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to transact: %d", result);
- return false;
+ ALOGE("IGBP::getFrameTimestamps failed to transact: %d", result);
+ return;
}
- bool found = false;
- result = reply.readBool(&found);
+ result = reply.read(*outDelta);
if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to read: %d", result);
- return false;
+ ALOGE("IGBP::getFrameTimestamps failed to read timestamps: %d",
+ result);
}
- if (found) {
- result = reply.read(*outTimestamps);
- if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to read timestamps: %d",
- result);
- return false;
- }
- }
- return found;
}
virtual status_t getUniqueId(uint64_t* outId) const {
@@ -522,14 +524,18 @@
uint32_t height = data.readUint32();
PixelFormat format = static_cast<PixelFormat>(data.readInt32());
uint32_t usage = data.readUint32();
+ bool getTimestamps = data.readBool();
+
int buf = 0;
- sp<Fence> fence;
+ sp<Fence> fence = Fence::NO_FENCE;
+ FrameEventHistoryDelta frameTimestamps;
int result = dequeueBuffer(&buf, &fence, width, height, format,
- usage);
+ usage, getTimestamps ? &frameTimestamps : nullptr);
+
reply->writeInt32(buf);
- reply->writeInt32(fence != NULL);
- if (fence != NULL) {
- reply->write(*fence);
+ reply->write(*fence);
+ if (getTimestamps) {
+ reply->write(frameTimestamps);
}
reply->writeInt32(result);
return NO_ERROR;
@@ -573,14 +579,14 @@
}
case QUEUE_BUFFER: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+
int buf = data.readInt32();
QueueBufferInput input(data);
- QueueBufferOutput* const output =
- reinterpret_cast<QueueBufferOutput *>(
- reply->writeInplace(sizeof(QueueBufferOutput)));
- memset(output, 0, sizeof(QueueBufferOutput));
- status_t result = queueBuffer(buf, input, output);
+ QueueBufferOutput output;
+ status_t result = queueBuffer(buf, input, &output);
+ reply->write(output);
reply->writeInt32(result);
+
return NO_ERROR;
}
case CANCEL_BUFFER: {
@@ -611,11 +617,9 @@
}
int api = data.readInt32();
bool producerControlledByApp = data.readInt32();
- QueueBufferOutput* const output =
- reinterpret_cast<QueueBufferOutput *>(
- reply->writeInplace(sizeof(QueueBufferOutput)));
- memset(output, 0, sizeof(QueueBufferOutput));
- status_t res = connect(listener, api, producerControlledByApp, output);
+ QueueBufferOutput output;
+ status_t res = connect(listener, api, producerControlledByApp, &output);
+ reply->write(output);
reply->writeInt32(res);
return NO_ERROR;
}
@@ -718,26 +722,14 @@
}
case GET_FRAME_TIMESTAMPS: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
- uint64_t frameNumber = 0;
- status_t result = data.readUint64(&frameNumber);
+ FrameEventHistoryDelta frameTimestamps;
+ getFrameTimestamps(&frameTimestamps);
+ status_t result = reply->write(frameTimestamps);
if (result != NO_ERROR) {
- ALOGE("onTransact failed to read: %d", result);
+ ALOGE("BnGBP::GET_FRAME_TIMESTAMPS failed to write buffer: %d",
+ result);
return result;
}
- FrameTimestamps timestamps;
- bool found = getFrameTimestamps(frameNumber, ×tamps);
- result = reply->writeBool(found);
- if (result != NO_ERROR) {
- ALOGE("onTransact failed to write: %d", result);
- return result;
- }
- if (found) {
- result = reply->write(timestamps);
- if (result != NO_ERROR) {
- ALOGE("onTransact failed to write timestamps: %d", result);
- return result;
- }
- }
return NO_ERROR;
}
case GET_UNIQUE_ID: {
@@ -764,16 +756,21 @@
parcel.read(*this);
}
+constexpr size_t IGraphicBufferProducer::QueueBufferInput::minFlattenedSize() {
+ return sizeof(timestamp) +
+ sizeof(isAutoTimestamp) +
+ sizeof(dataSpace) +
+ sizeof(crop) +
+ sizeof(scalingMode) +
+ sizeof(transform) +
+ sizeof(stickyTransform) +
+ sizeof(getFrameTimestamps);
+}
+
size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const {
- return sizeof(timestamp)
- + sizeof(isAutoTimestamp)
- + sizeof(dataSpace)
- + sizeof(crop)
- + sizeof(scalingMode)
- + sizeof(transform)
- + sizeof(stickyTransform)
- + fence->getFlattenedSize()
- + surfaceDamage.getFlattenedSize();
+ return minFlattenedSize() +
+ fence->getFlattenedSize() +
+ surfaceDamage.getFlattenedSize();
}
size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const {
@@ -786,6 +783,7 @@
if (size < getFlattenedSize()) {
return NO_MEMORY;
}
+
FlattenableUtils::write(buffer, size, timestamp);
FlattenableUtils::write(buffer, size, isAutoTimestamp);
FlattenableUtils::write(buffer, size, dataSpace);
@@ -793,6 +791,8 @@
FlattenableUtils::write(buffer, size, scalingMode);
FlattenableUtils::write(buffer, size, transform);
FlattenableUtils::write(buffer, size, stickyTransform);
+ FlattenableUtils::write(buffer, size, getFrameTimestamps);
+
status_t result = fence->flatten(buffer, size, fds, count);
if (result != NO_ERROR) {
return result;
@@ -803,16 +803,7 @@
status_t IGraphicBufferProducer::QueueBufferInput::unflatten(
void const*& buffer, size_t& size, int const*& fds, size_t& count)
{
- size_t minNeeded =
- sizeof(timestamp)
- + sizeof(isAutoTimestamp)
- + sizeof(dataSpace)
- + sizeof(crop)
- + sizeof(scalingMode)
- + sizeof(transform)
- + sizeof(stickyTransform);
-
- if (size < minNeeded) {
+ if (size < minFlattenedSize()) {
return NO_MEMORY;
}
@@ -823,6 +814,7 @@
FlattenableUtils::read(buffer, size, scalingMode);
FlattenableUtils::read(buffer, size, transform);
FlattenableUtils::read(buffer, size, stickyTransform);
+ FlattenableUtils::read(buffer, size, getFrameTimestamps);
fence = new Fence();
status_t result = fence->unflatten(buffer, size, fds, count);
@@ -832,4 +824,53 @@
return surfaceDamage.unflatten(buffer, size);
}
+// ----------------------------------------------------------------------------
+constexpr size_t IGraphicBufferProducer::QueueBufferOutput::minFlattenedSize() {
+ return sizeof(width) +
+ sizeof(height) +
+ sizeof(transformHint) +
+ sizeof(numPendingBuffers) +
+ sizeof(nextFrameNumber);
+}
+
+size_t IGraphicBufferProducer::QueueBufferOutput::getFlattenedSize() const {
+ return minFlattenedSize() + frameTimestamps.getFlattenedSize();
+}
+
+size_t IGraphicBufferProducer::QueueBufferOutput::getFdCount() const {
+ return frameTimestamps.getFdCount();
+}
+
+status_t IGraphicBufferProducer::QueueBufferOutput::flatten(
+ void*& buffer, size_t& size, int*& fds, size_t& count) const
+{
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(buffer, size, width);
+ FlattenableUtils::write(buffer, size, height);
+ FlattenableUtils::write(buffer, size, transformHint);
+ FlattenableUtils::write(buffer, size, numPendingBuffers);
+ FlattenableUtils::write(buffer, size, nextFrameNumber);
+
+ return frameTimestamps.flatten(buffer, size, fds, count);
+}
+
+status_t IGraphicBufferProducer::QueueBufferOutput::unflatten(
+ void const*& buffer, size_t& size, int const*& fds, size_t& count)
+{
+ if (size < minFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, width);
+ FlattenableUtils::read(buffer, size, height);
+ FlattenableUtils::read(buffer, size, transformHint);
+ FlattenableUtils::read(buffer, size, numPendingBuffers);
+ FlattenableUtils::read(buffer, size, nextFrameNumber);
+
+ return frameTimestamps.unflatten(buffer, size, fds, count);
+}
+
}; // namespace android
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 6c1662c..fa2f59a 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -158,6 +158,50 @@
return result != 0;
}
+ virtual status_t getSupportedFrameTimestamps(
+ std::vector<FrameEvent>* outSupported) const {
+ if (!outSupported) {
+ return UNEXPECTED_NULL;
+ }
+ outSupported->clear();
+
+ Parcel data, reply;
+
+ status_t err = data.writeInterfaceToken(
+ ISurfaceComposer::getInterfaceDescriptor());
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ err = remote()->transact(
+ BnSurfaceComposer::GET_SUPPORTED_FRAME_TIMESTAMPS,
+ data, &reply);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ int32_t result = 0;
+ err = reply.readInt32(&result);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ if (result != NO_ERROR) {
+ return result;
+ }
+
+ std::vector<int32_t> supported;
+ err = reply.readInt32Vector(&supported);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ outSupported->reserve(supported.size());
+ for (int32_t s : supported) {
+ outSupported->push_back(static_cast<FrameEvent>(s));
+ }
+ return NO_ERROR;
+ }
+
virtual sp<IDisplayEventConnection> createDisplayEventConnection()
{
Parcel data, reply;
@@ -520,6 +564,25 @@
reply->writeInt32(result);
return NO_ERROR;
}
+ case GET_SUPPORTED_FRAME_TIMESTAMPS: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ std::vector<FrameEvent> supportedTimestamps;
+ status_t result = getSupportedFrameTimestamps(&supportedTimestamps);
+ status_t err = reply->writeInt32(result);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ if (result != NO_ERROR) {
+ return result;
+ }
+
+ std::vector<int32_t> supported;
+ supported.reserve(supportedTimestamps.size());
+ for (FrameEvent s : supportedTimestamps) {
+ supported.push_back(static_cast<int32_t>(s));
+ }
+ return reply->writeInt32Vector(supported);
+ }
case CREATE_DISPLAY_EVENT_CONNECTION: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IDisplayEventConnection> connection(createDisplayEventConnection());
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index ac93c53..6a02a77 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -49,7 +49,11 @@
mAutoRefresh(false),
mSharedBufferSlot(BufferItem::INVALID_BUFFER_SLOT),
mSharedBufferHasBeenQueued(false),
- mNextFrameNumber(1)
+ mNextFrameNumber(1),
+ mQueriedSupportedTimestamps(false),
+ mFrameTimestampsSupportsPresent(false),
+ mFrameTimestampsSupportsRetire(false),
+ mEnableFrameTimestamps(false)
{
// Initialize the ANativeWindow function pointers.
ANativeWindow::setSwapInterval = hook_setSwapInterval;
@@ -135,37 +139,80 @@
outTransformMatrix);
}
-bool Surface::getFrameTimestamps(uint64_t frameNumber,
+void Surface::enableFrameTimestamps(bool enable) {
+ Mutex::Autolock lock(mMutex);
+ mEnableFrameTimestamps = enable;
+}
+
+status_t Surface::getFrameTimestamps(uint64_t frameNumber,
nsecs_t* outRequestedPresentTime, nsecs_t* outAcquireTime,
nsecs_t* outRefreshStartTime, nsecs_t* outGlCompositionDoneTime,
- nsecs_t* outDisplayRetireTime, nsecs_t* outReleaseTime) {
+ nsecs_t* outDisplayPresentTime, nsecs_t* outDisplayRetireTime,
+ nsecs_t* outReleaseTime) {
ATRACE_CALL();
- FrameTimestamps timestamps;
- bool found = mGraphicBufferProducer->getFrameTimestamps(frameNumber,
- ×tamps);
- if (found) {
- if (outRequestedPresentTime) {
- *outRequestedPresentTime = timestamps.requestedPresentTime;
- }
- if (outAcquireTime) {
- *outAcquireTime = timestamps.acquireTime;
- }
- if (outRefreshStartTime) {
- *outRefreshStartTime = timestamps.refreshStartTime;
- }
- if (outGlCompositionDoneTime) {
- *outGlCompositionDoneTime = timestamps.glCompositionDoneTime;
- }
- if (outDisplayRetireTime) {
- *outDisplayRetireTime = timestamps.displayRetireTime;
- }
- if (outReleaseTime) {
- *outReleaseTime = timestamps.releaseTime;
- }
- return true;
+ Mutex::Autolock lock(mMutex);
+
+ if (!mEnableFrameTimestamps) {
+ return INVALID_OPERATION;
}
- return false;
+
+ // Verify the requested timestamps are supported.
+ querySupportedTimestampsLocked();
+ if (outDisplayPresentTime != nullptr && !mFrameTimestampsSupportsPresent) {
+ return BAD_VALUE;
+ }
+ if (outDisplayRetireTime != nullptr && !mFrameTimestampsSupportsRetire) {
+ return BAD_VALUE;
+ }
+
+ FrameEvents* events = mFrameEventHistory.getFrame(frameNumber);
+
+ // Update our cache of events if the requested events are not available.
+ if (events == nullptr ||
+ (outRequestedPresentTime && !events->hasRequestedPresentInfo()) ||
+ (outAcquireTime && !events->hasAcquireInfo()) ||
+ (outRefreshStartTime && !events->hasFirstRefreshStartInfo()) ||
+ (outGlCompositionDoneTime && !events->hasGpuCompositionDoneInfo()) ||
+ (outDisplayPresentTime && !events->hasDisplayPresentInfo()) ||
+ (outDisplayRetireTime && !events->hasDisplayRetireInfo()) ||
+ (outReleaseTime && !events->hasReleaseInfo())) {
+ FrameEventHistoryDelta delta;
+ mGraphicBufferProducer->getFrameTimestamps(&delta);
+ mFrameEventHistory.applyDelta(delta);
+ events = mFrameEventHistory.getFrame(frameNumber);
+ }
+
+ // A record for the requested frame does not exist.
+ if (events == nullptr) {
+ return NAME_NOT_FOUND;
+ }
+
+ events->checkFencesForCompletion();
+
+ if (outRequestedPresentTime) {
+ *outRequestedPresentTime = events->requestedPresentTime;
+ }
+ if (outAcquireTime) {
+ *outAcquireTime = events->acquireTime;
+ }
+ if (outRefreshStartTime) {
+ *outRefreshStartTime = events->firstRefreshStartTime;
+ }
+ if (outGlCompositionDoneTime) {
+ *outGlCompositionDoneTime = events->gpuCompositionDoneTime;
+ }
+ if (outDisplayPresentTime) {
+ *outDisplayPresentTime = events->displayPresentTime;
+ }
+ if (outDisplayRetireTime) {
+ *outDisplayRetireTime = events->displayRetireTime;
+ }
+ if (outReleaseTime) {
+ *outReleaseTime = events->releaseTime;
+ }
+
+ return NO_ERROR;
}
int Surface::hook_setSwapInterval(ANativeWindow* window, int interval) {
@@ -271,6 +318,7 @@
uint32_t reqHeight;
PixelFormat reqFormat;
uint32_t reqUsage;
+ bool enableFrameTimestamps;
{
Mutex::Autolock lock(mMutex);
@@ -281,6 +329,8 @@
reqFormat = mReqFormat;
reqUsage = mReqUsage;
+ enableFrameTimestamps = mEnableFrameTimestamps;
+
if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot !=
BufferItem::INVALID_BUFFER_SLOT) {
sp<GraphicBuffer>& gbuf(mSlots[mSharedBufferSlot].buffer);
@@ -295,8 +345,13 @@
int buf = -1;
sp<Fence> fence;
nsecs_t now = systemTime();
+
+ FrameEventHistoryDelta frameTimestamps;
+ FrameEventHistoryDelta* frameTimestampsOrNull =
+ enableFrameTimestamps ? &frameTimestamps : nullptr;
+
status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence,
- reqWidth, reqHeight, reqFormat, reqUsage);
+ reqWidth, reqHeight, reqFormat, reqUsage, frameTimestampsOrNull);
mLastDequeueDuration = systemTime() - now;
if (result < 0) {
@@ -317,6 +372,10 @@
freeAllBuffers();
}
+ if (enableFrameTimestamps) {
+ mFrameEventHistory.applyDelta(frameTimestamps);
+ }
+
if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) {
result = mGraphicBufferProducer->requestBuffer(buf, &gbuf);
if (result != NO_ERROR) {
@@ -435,7 +494,7 @@
IGraphicBufferProducer::QueueBufferOutput output;
IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp,
mDataSpace, crop, mScalingMode, mTransform ^ mStickyTransform,
- fence, mStickyTransform);
+ fence, mStickyTransform, mEnableFrameTimestamps);
if (mConnectedToCpu || mDirtyRegion.bounds() == Rect::INVALID_RECT) {
input.setSurfaceDamage(Region::INVALID_REGION);
@@ -507,17 +566,24 @@
ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);
}
- uint32_t numPendingBuffers = 0;
- uint32_t hint = 0;
- output.deflate(&mDefaultWidth, &mDefaultHeight, &hint,
- &numPendingBuffers, &mNextFrameNumber);
+ if (mEnableFrameTimestamps) {
+ mFrameEventHistory.applyDelta(output.frameTimestamps);
+ // Update timestamps with the local acquire fence.
+ // The consumer doesn't send it back to prevent us from having two
+ // file descriptors of the same fence.
+ mFrameEventHistory.updateAcquireFence(mNextFrameNumber, fence);
+ }
+
+ mDefaultWidth = output.width;
+ mDefaultHeight = output.height;
+ mNextFrameNumber = output.nextFrameNumber;
// Disable transform hint if sticky transform is set.
if (mStickyTransform == 0) {
- mTransformHint = hint;
+ mTransformHint = output.transformHint;
}
- mConsumerRunningBehind = (numPendingBuffers >= 2);
+ mConsumerRunningBehind = (output.numPendingBuffers >= 2);
if (!mConnectedToCpu) {
// Clear surface damage back to full-buffer
@@ -533,6 +599,32 @@
return err;
}
+void Surface::querySupportedTimestampsLocked() const {
+ // mMutex must be locked when calling this method.
+
+ if (mQueriedSupportedTimestamps) {
+ return;
+ }
+ mQueriedSupportedTimestamps = true;
+
+ std::vector<FrameEvent> supportedFrameTimestamps;
+ sp<ISurfaceComposer> composer(ComposerService::getComposerService());
+ status_t err = composer->getSupportedFrameTimestamps(
+ &supportedFrameTimestamps);
+
+ if (err != NO_ERROR) {
+ return;
+ }
+
+ for (auto sft : supportedFrameTimestamps) {
+ if (sft == FrameEvent::DISPLAY_PRESENT) {
+ mFrameTimestampsSupportsPresent = true;
+ } else if (sft == FrameEvent::DISPLAY_RETIRE) {
+ mFrameTimestampsSupportsRetire = true;
+ }
+ }
+}
+
int Surface::query(int what, int* value) const {
ATRACE_CALL();
ALOGV("Surface::query");
@@ -595,6 +687,16 @@
static_cast<int>(durationUs);
return NO_ERROR;
}
+ case NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT: {
+ querySupportedTimestampsLocked();
+ *value = mFrameTimestampsSupportsPresent ? 1 : 0;
+ return NO_ERROR;
+ }
+ case NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_RETIRE: {
+ querySupportedTimestampsLocked();
+ *value = mFrameTimestampsSupportsRetire ? 1 : 0;
+ return NO_ERROR;
+ }
}
}
return mGraphicBufferProducer->query(what, value);
@@ -670,6 +772,9 @@
case NATIVE_WINDOW_SET_AUTO_REFRESH:
res = dispatchSetAutoRefresh(args);
break;
+ case NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS:
+ res = dispatchEnableFrameTimestamps(args);
+ break;
case NATIVE_WINDOW_GET_FRAME_TIMESTAMPS:
res = dispatchGetFrameTimestamps(args);
break;
@@ -793,18 +898,25 @@
return setAutoRefresh(autoRefresh);
}
+int Surface::dispatchEnableFrameTimestamps(va_list args) {
+ bool enable = va_arg(args, int);
+ enableFrameTimestamps(enable);
+ return NO_ERROR;
+}
+
int Surface::dispatchGetFrameTimestamps(va_list args) {
uint32_t framesAgo = va_arg(args, uint32_t);
nsecs_t* outRequestedPresentTime = va_arg(args, int64_t*);
nsecs_t* outAcquireTime = va_arg(args, int64_t*);
nsecs_t* outRefreshStartTime = va_arg(args, int64_t*);
nsecs_t* outGlCompositionDoneTime = va_arg(args, int64_t*);
+ nsecs_t* outDisplayPresentTime = va_arg(args, int64_t*);
nsecs_t* outDisplayRetireTime = va_arg(args, int64_t*);
nsecs_t* outReleaseTime = va_arg(args, int64_t*);
- bool ret = getFrameTimestamps(getNextFrameNumber() - 1 - framesAgo,
+ return getFrameTimestamps(getNextFrameNumber() - 1 - framesAgo,
outRequestedPresentTime, outAcquireTime, outRefreshStartTime,
- outGlCompositionDoneTime, outDisplayRetireTime, outReleaseTime);
- return ret ? NO_ERROR : BAD_VALUE;
+ outGlCompositionDoneTime, outDisplayPresentTime,
+ outDisplayRetireTime, outReleaseTime);
}
int Surface::connect(int api) {
@@ -819,17 +931,16 @@
IGraphicBufferProducer::QueueBufferOutput output;
int err = mGraphicBufferProducer->connect(listener, api, mProducerControlledByApp, &output);
if (err == NO_ERROR) {
- uint32_t numPendingBuffers = 0;
- uint32_t hint = 0;
- output.deflate(&mDefaultWidth, &mDefaultHeight, &hint,
- &numPendingBuffers, &mNextFrameNumber);
+ mDefaultWidth = output.width;
+ mDefaultHeight = output.height;
+ mNextFrameNumber = output.nextFrameNumber;
// Disable transform hint if sticky transform is set.
if (mStickyTransform == 0) {
- mTransformHint = hint;
+ mTransformHint = output.transformHint;
}
- mConsumerRunningBehind = (numPendingBuffers >= 2);
+ mConsumerRunningBehind = (output.numPendingBuffers >= 2);
}
if (!err && api == NATIVE_WINDOW_API_CPU) {
mConnectedToCpu = true;
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 65df7dc..98c0449 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -139,7 +139,7 @@
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
uint32_t* dataIn;
@@ -183,7 +183,7 @@
for (int i = 0; i < 2; i++) {
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0,
- GRALLOC_USAGE_SW_READ_OFTEN));
+ GRALLOC_USAGE_SW_READ_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
@@ -191,7 +191,7 @@
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0,
- GRALLOC_USAGE_SW_READ_OFTEN));
+ GRALLOC_USAGE_SW_READ_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
@@ -234,7 +234,7 @@
for (int i = 0; i < 3; i++) {
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0,
- GRALLOC_USAGE_SW_READ_OFTEN));
+ GRALLOC_USAGE_SW_READ_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
@@ -270,7 +270,7 @@
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0,
- GRALLOC_USAGE_SW_READ_OFTEN));
+ GRALLOC_USAGE_SW_READ_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
@@ -280,7 +280,7 @@
for (int i = 0; i < 2; i++) {
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0,
- GRALLOC_USAGE_SW_READ_OFTEN));
+ GRALLOC_USAGE_SW_READ_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
@@ -330,7 +330,7 @@
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
ASSERT_EQ(BAD_VALUE, mProducer->detachBuffer(slot)); // Not requested
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
ASSERT_EQ(OK, mProducer->detachBuffer(slot));
@@ -379,7 +379,7 @@
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
IGraphicBufferProducer::QueueBufferInput input(0, false,
HAL_DATASPACE_UNKNOWN, Rect(0, 0, 1, 1),
@@ -415,7 +415,7 @@
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
uint32_t* dataOut;
@@ -438,7 +438,7 @@
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
uint32_t* dataIn;
@@ -487,13 +487,13 @@
// This should return an error since it would require an allocation
ASSERT_EQ(OK, mProducer->allowAllocation(false));
ASSERT_EQ(WOULD_BLOCK, mProducer->dequeueBuffer(&slot, &fence, 0, 0,
- 0, GRALLOC_USAGE_SW_WRITE_OFTEN));
+ 0, GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
// This should succeed, now that we've lifted the prohibition
ASSERT_EQ(OK, mProducer->allowAllocation(true));
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
// Release the previous buffer back to the BufferQueue
mProducer->cancelBuffer(slot, fence);
@@ -501,7 +501,7 @@
// This should fail since we're requesting a different size
ASSERT_EQ(OK, mProducer->allowAllocation(false));
ASSERT_EQ(WOULD_BLOCK, mProducer->dequeueBuffer(&slot, &fence,
- WIDTH * 2, HEIGHT * 2, 0, GRALLOC_USAGE_SW_WRITE_OFTEN));
+ WIDTH * 2, HEIGHT * 2, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
}
TEST_F(BufferQueueTest, TestGenerationNumbers) {
@@ -518,7 +518,7 @@
int slot;
sp<Fence> fence;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
sp<GraphicBuffer> buffer;
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
@@ -561,7 +561,7 @@
sp<Fence> fence;
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0));
+ mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(sharedSlot, &buffer));
// Queue the buffer
@@ -575,7 +575,8 @@
// always the same one and because async mode gets enabled.
int slot;
for (int i = 0; i < 5; i++) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(sharedSlot, slot);
ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output));
}
@@ -612,7 +613,7 @@
sp<Fence> fence;
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0));
+ mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(sharedSlot, &buffer));
// Queue the buffer
@@ -639,7 +640,8 @@
// always return the same one.
int slot;
for (int i = 0; i < 5; i++) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(sharedSlot, slot);
ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output));
}
@@ -678,7 +680,7 @@
sp<Fence> fence;
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0));
+ mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(sharedSlot, &buffer));
// Enable shared buffer mode
@@ -695,7 +697,8 @@
// always the same one and because async mode gets enabled.
int slot;
for (int i = 0; i < 5; i++) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(sharedSlot, slot);
ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output));
}
@@ -730,7 +733,8 @@
for (int i = 0; i < 5; ++i) {
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> fence = Fence::NO_FENCE;
- auto result = mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0);
+ auto result = mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr);
if (i < 2) {
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
result);
@@ -757,7 +761,8 @@
for (int i = 0; i < 2; ++i) {
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> fence = Fence::NO_FENCE;
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
IGraphicBufferProducer::QueueBufferInput input(0ull, true,
HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT,
@@ -768,7 +773,8 @@
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> fence = Fence::NO_FENCE;
auto startTime = systemTime();
- ASSERT_EQ(TIMED_OUT, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(TIMED_OUT, mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_GE(systemTime() - startTime, TIMEOUT);
// We're technically attaching the same buffer multiple times (since we
@@ -789,7 +795,7 @@
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> sourceFence;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&slot, &sourceFence, 0, 0, 0, 0));
+ mProducer->dequeueBuffer(&slot, &sourceFence, 0, 0, 0, 0, nullptr));
sp<GraphicBuffer> buffer;
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
ASSERT_EQ(OK, mProducer->detachBuffer(slot));
@@ -812,7 +818,7 @@
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> fence;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
sp<GraphicBuffer> firstBuffer;
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &firstBuffer));
@@ -824,7 +830,7 @@
// Dequeue a second buffer
slot = BufferQueue::INVALID_BUFFER_SLOT;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
sp<GraphicBuffer> secondBuffer;
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &secondBuffer));
@@ -876,7 +882,7 @@
mProducer->setMaxDequeuedBufferCount(3);
for (size_t i = 0; i < 3; ++i) {
status_t result = mProducer->dequeueBuffer(&slots[i], &fence,
- 0, 0, 0, 0);
+ 0, 0, 0, 0, nullptr);
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer));
}
@@ -889,7 +895,8 @@
// The first segment is a two-buffer segment, so we only put one buffer into
// the queue at a time
for (size_t i = 0; i < 5; ++i) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
@@ -904,16 +911,17 @@
// two-buffer segment, but then at the end, we put two buffers in the queue
// at the same time before draining it.
for (size_t i = 0; i < 5; ++i) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
std::this_thread::sleep_for(16ms);
}
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
@@ -928,10 +936,11 @@
// The third segment is a triple-buffer segment, so the queue is switching
// between one buffer and two buffers deep.
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
for (size_t i = 0; i < 5; ++i) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
@@ -1012,7 +1021,7 @@
mProducer->setMaxDequeuedBufferCount(4);
for (size_t i = 0; i < 4; ++i) {
status_t result = mProducer->dequeueBuffer(&slots[i], &fence,
- 0, 0, 0, 0);
+ 0, 0, 0, 0, nullptr);
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer));
}
@@ -1023,14 +1032,14 @@
// Get buffers in all states: dequeued, filled, acquired, free
// Fill 3 buffers
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
// Dequeue 1 buffer
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
// Acquire and free 1 buffer
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp
index 9f33047..0329a6d 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -196,7 +196,7 @@
};
status_t dequeueBuffer(uint32_t w, uint32_t h, uint32_t format, uint32_t usage, DequeueBufferResult* result) {
- return mProducer->dequeueBuffer(&result->slot, &result->fence, w, h, format, usage);
+ return mProducer->dequeueBuffer(&result->slot, &result->fence, w, h, format, usage, nullptr);
}
void setupDequeueRequestBuffer(int *slot, sp<Fence> *fence,
@@ -210,7 +210,7 @@
ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
(mProducer->dequeueBuffer(slot, fence, DEFAULT_WIDTH,
- DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS)));
+ DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS, nullptr)));
EXPECT_LE(0, *slot);
EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, *slot);
@@ -349,7 +349,7 @@
ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
(mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
- TEST_PRODUCER_USAGE_BITS)));
+ TEST_PRODUCER_USAGE_BITS, nullptr)));
EXPECT_LE(0, dequeuedSlot);
EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, dequeuedSlot);
@@ -366,20 +366,12 @@
ASSERT_OK(mProducer->queueBuffer(dequeuedSlot, input, &output));
{
- uint32_t width;
- uint32_t height;
- uint32_t transformHint;
- uint32_t numPendingBuffers;
- uint64_t nextFrameNumber;
-
- output.deflate(&width, &height, &transformHint, &numPendingBuffers,
- &nextFrameNumber);
-
- EXPECT_EQ(DEFAULT_WIDTH, width);
- EXPECT_EQ(DEFAULT_HEIGHT, height);
- EXPECT_EQ(DEFAULT_TRANSFORM_HINT, transformHint);
- EXPECT_EQ(1u, numPendingBuffers); // since queueBuffer was called exactly once
- EXPECT_EQ(2u, nextFrameNumber);
+ EXPECT_EQ(DEFAULT_WIDTH, output.width);
+ EXPECT_EQ(DEFAULT_HEIGHT, output.height);
+ EXPECT_EQ(DEFAULT_TRANSFORM_HINT, output.transformHint);
+ // Since queueBuffer was called exactly once
+ EXPECT_EQ(1u, output.numPendingBuffers);
+ EXPECT_EQ(2u, output.nextFrameNumber);
}
// Buffer was not in the dequeued state
@@ -416,7 +408,7 @@
ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
(mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
- TEST_PRODUCER_USAGE_BITS)));
+ TEST_PRODUCER_USAGE_BITS, nullptr)));
// Slot was enqueued without requesting a buffer
{
@@ -485,7 +477,7 @@
ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
(mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
- TEST_PRODUCER_USAGE_BITS)));
+ TEST_PRODUCER_USAGE_BITS, nullptr)));
// No return code, but at least test that it doesn't blow up...
// TODO: add a return code
@@ -534,7 +526,7 @@
(mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
DEFAULT_WIDTH, DEFAULT_HEIGHT,
DEFAULT_FORMAT,
- TEST_PRODUCER_USAGE_BITS)))
+ TEST_PRODUCER_USAGE_BITS, nullptr)))
<< "iteration: " << i << ", slot: " << dequeuedSlot;
}
@@ -571,7 +563,7 @@
(mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
DEFAULT_WIDTH, DEFAULT_HEIGHT,
DEFAULT_FORMAT,
- TEST_PRODUCER_USAGE_BITS)))
+ TEST_PRODUCER_USAGE_BITS, nullptr)))
<< "slot: " << dequeuedSlot;
}
@@ -606,7 +598,8 @@
ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
(mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
- TEST_PRODUCER_USAGE_BITS))) << "slot : " << dequeuedSlot;
+ TEST_PRODUCER_USAGE_BITS, nullptr)))
+ << "slot : " << dequeuedSlot;
ASSERT_OK(mProducer->requestBuffer(dequeuedSlot, &dequeuedBuffer));
ASSERT_OK(mProducer->queueBuffer(dequeuedSlot, input, &output));
}
@@ -622,7 +615,8 @@
ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
(mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
- TEST_PRODUCER_USAGE_BITS))) << "slot: " << dequeuedSlot;
+ TEST_PRODUCER_USAGE_BITS, nullptr)))
+ << "slot: " << dequeuedSlot;
}
// Abandon buffer queue
@@ -639,7 +633,7 @@
sp<Fence> fence;
ASSERT_EQ(NO_INIT, mProducer->dequeueBuffer(&slot, &fence, DEFAULT_WIDTH,
- DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS));
+ DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS, nullptr));
}
TEST_F(IGraphicBufferProducerTest,
@@ -659,7 +653,8 @@
ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
(mProducer->dequeueBuffer(&slot, &fence, DEFAULT_WIDTH,
- DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS)));
+ DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS,
+ nullptr)));
EXPECT_LE(0, slot);
EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, slot);
diff --git a/libs/gui/tests/StreamSplitter_test.cpp b/libs/gui/tests/StreamSplitter_test.cpp
index 498492e..80e30da 100644
--- a/libs/gui/tests/StreamSplitter_test.cpp
+++ b/libs/gui/tests/StreamSplitter_test.cpp
@@ -81,7 +81,7 @@
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
ASSERT_EQ(OK, inputProducer->requestBuffer(slot, &buffer));
uint32_t* dataIn;
@@ -115,7 +115,7 @@
// received the buffer back from the output BufferQueue
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
}
TEST_F(StreamSplitterTest, OneInputMultipleOutputs) {
@@ -153,7 +153,7 @@
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
ASSERT_EQ(OK, inputProducer->requestBuffer(slot, &buffer));
uint32_t* dataIn;
@@ -190,7 +190,7 @@
// received the buffer back from the output BufferQueues
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
}
TEST_F(StreamSplitterTest, OutputAbandonment) {
@@ -217,7 +217,7 @@
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
ASSERT_EQ(OK, inputProducer->requestBuffer(slot, &buffer));
// Abandon the output
@@ -230,7 +230,7 @@
// Input should be abandoned
ASSERT_EQ(NO_INIT, inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
}
} // namespace android
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 0adfdea..3328a92 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -43,6 +43,7 @@
srcs: [
"ColorSpace.cpp",
"Fence.cpp",
+ "FenceTime.cpp",
"FrameStats.cpp",
"Gralloc1.cpp",
"Gralloc1On0Adapter.cpp",
diff --git a/libs/ui/Fence.cpp b/libs/ui/Fence.cpp
index 7cf8233..a1dda3a 100644
--- a/libs/ui/Fence.cpp
+++ b/libs/ui/Fence.cpp
@@ -109,17 +109,17 @@
nsecs_t Fence::getSignalTime() const {
if (mFenceFd == -1) {
- return -1;
+ return SIGNAL_TIME_INVALID;
}
struct sync_fence_info_data* finfo = sync_fence_info(mFenceFd);
if (finfo == NULL) {
ALOGE("sync_fence_info returned NULL for fd %d", mFenceFd);
- return -1;
+ return SIGNAL_TIME_INVALID;
}
if (finfo->status != 1) {
sync_fence_info_free(finfo);
- return INT64_MAX;
+ return SIGNAL_TIME_PENDING;
}
struct sync_pt_info* pinfo = NULL;
diff --git a/libs/ui/FenceTime.cpp b/libs/ui/FenceTime.cpp
new file mode 100644
index 0000000..c0245eb
--- /dev/null
+++ b/libs/ui/FenceTime.cpp
@@ -0,0 +1,282 @@
+/*
+* Copyright 2016 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 <ui/FenceTime.h>
+
+#include <cutils/compiler.h> // For CC_[UN]LIKELY
+#include <inttypes.h>
+#include <stdlib.h>
+
+#include <memory>
+
+namespace android {
+
+// ============================================================================
+// FenceTime
+// ============================================================================
+
+const auto FenceTime::NO_FENCE = std::make_shared<FenceTime>(Fence::NO_FENCE);
+
+void* FenceTime::operator new(size_t byteCount) noexcept {
+ void *p = nullptr;
+ if (posix_memalign(&p, alignof(FenceTime), byteCount)) {
+ return nullptr;
+ }
+ return p;
+}
+
+void FenceTime::operator delete(void *p) {
+ free(p);
+}
+
+FenceTime::FenceTime(const sp<Fence>& fence)
+ : mState(((fence.get() != nullptr) && fence->isValid()) ?
+ State::VALID : State::INVALID),
+ mFence(fence),
+ mSignalTime(mState == State::INVALID ?
+ Fence::SIGNAL_TIME_INVALID : Fence::SIGNAL_TIME_PENDING) {
+}
+
+FenceTime::FenceTime(sp<Fence>&& fence)
+ : mState(((fence.get() != nullptr) && fence->isValid()) ?
+ State::VALID : State::INVALID),
+ mFence(std::move(fence)),
+ mSignalTime(mState == State::INVALID ?
+ Fence::SIGNAL_TIME_INVALID : Fence::SIGNAL_TIME_PENDING) {
+}
+
+FenceTime::FenceTime(nsecs_t signalTime)
+ : mState(Fence::isValidTimestamp(signalTime) ? State::VALID : State::INVALID),
+ mFence(nullptr),
+ mSignalTime(signalTime == Fence::SIGNAL_TIME_PENDING ?
+ Fence::SIGNAL_TIME_INVALID : signalTime) {
+}
+
+void FenceTime::applyTrustedSnapshot(const Snapshot& src) {
+ if (CC_UNLIKELY(src.state != Snapshot::State::SIGNAL_TIME)) {
+ // Applying Snapshot::State::FENCE, could change the valid state of the
+ // FenceTime, which is not allowed. Callers should create a new
+ // FenceTime from the snapshot instead.
+ ALOGE("FenceTime::applyTrustedSnapshot: Unexpected fence.");
+ return;
+ }
+
+ if (src.state == Snapshot::State::EMPTY) {
+ return;
+ }
+
+ nsecs_t signalTime = mSignalTime.load(std::memory_order_relaxed);
+ if (signalTime != Fence::SIGNAL_TIME_PENDING) {
+ // We should always get the same signalTime here that we did in
+ // getSignalTime(). This check races with getSignalTime(), but it is
+ // only a sanity check so that's okay.
+ if (CC_UNLIKELY(signalTime != src.signalTime)) {
+ ALOGE("FenceTime::applyTrustedSnapshot: signalTime mismatch. "
+ "(%" PRId64 " (old) != %" PRId64 " (new))",
+ signalTime, src.signalTime);
+ }
+ return;
+ }
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ mFence.clear();
+ mSignalTime.store(src.signalTime, std::memory_order_relaxed);
+}
+
+bool FenceTime::isValid() const {
+ // We store the valid state in the constructors and return it here.
+ // This lets release code remember the valid state even after the
+ // underlying fence is destroyed.
+ return mState != State::INVALID;
+}
+
+nsecs_t FenceTime::getSignalTime() {
+ // See if we already have a cached value we can return.
+ nsecs_t signalTime = mSignalTime.load(std::memory_order_relaxed);
+ if (signalTime != Fence::SIGNAL_TIME_PENDING) {
+ return signalTime;
+ }
+
+ // Hold a reference to the fence on the stack in case the class'
+ // reference is removed by another thread. This prevents the
+ // fence from being destroyed until the end of this method, where
+ // we conveniently do not have the lock held.
+ sp<Fence> fence;
+ {
+ // With the lock acquired this time, see if we have the cached
+ // value or if we need to poll the fence.
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!mFence.get()) {
+ // Another thread set the signal time just before we added the
+ // reference to mFence.
+ return mSignalTime.load(std::memory_order_relaxed);
+ }
+ fence = mFence;
+ }
+
+ // Make the system call without the lock held.
+ signalTime = fence->getSignalTime();
+
+ // Make the signal time visible to everyone if it is no longer pending
+ // and remove the class' reference to the fence.
+ if (signalTime != Fence::SIGNAL_TIME_PENDING) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mFence.clear();
+ mSignalTime.store(signalTime, std::memory_order_relaxed);
+ }
+
+ return signalTime;
+}
+
+nsecs_t FenceTime::getCachedSignalTime() const {
+ // memory_order_acquire since we don't have a lock fallback path
+ // that will do an acquire.
+ return mSignalTime.load(std::memory_order_acquire);
+}
+
+FenceTime::Snapshot FenceTime::getSnapshot() const {
+ // Quick check without the lock.
+ nsecs_t signalTime = mSignalTime.load(std::memory_order_relaxed);
+ if (signalTime != Fence::SIGNAL_TIME_PENDING) {
+ return Snapshot(signalTime);
+ }
+
+ // Do the full check with the lock.
+ std::lock_guard<std::mutex> lock(mMutex);
+ signalTime = mSignalTime.load(std::memory_order_relaxed);
+ if (signalTime != Fence::SIGNAL_TIME_PENDING) {
+ return Snapshot(signalTime);
+ }
+ return Snapshot(mFence);
+}
+
+// ============================================================================
+// FenceTime::Snapshot
+// ============================================================================
+
+FenceTime::Snapshot::Snapshot(const sp<Fence>& srcFence)
+ : state(State::FENCE), fence(srcFence) {
+}
+
+FenceTime::Snapshot::Snapshot(nsecs_t srcSignalTime)
+ : state(State::SIGNAL_TIME), signalTime(srcSignalTime) {
+}
+
+size_t FenceTime::Snapshot::getFlattenedSize() const {
+ constexpr size_t min = sizeof(state);
+ switch (state) {
+ case State::EMPTY:
+ return min;
+ case State::FENCE:
+ return min + fence->getFlattenedSize();
+ case State::SIGNAL_TIME:
+ return min + sizeof(signalTime);
+ }
+ return 0;
+}
+
+size_t FenceTime::Snapshot::getFdCount() const {
+ return state == State::FENCE ? fence->getFdCount() : 0u;
+}
+
+status_t FenceTime::Snapshot::flatten(
+ void*& buffer, size_t& size, int*& fds, size_t& count) const {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(buffer, size, state);
+ switch (state) {
+ case State::EMPTY:
+ return NO_ERROR;
+ case State::FENCE:
+ return fence->flatten(buffer, size, fds, count);
+ case State::SIGNAL_TIME:
+ FlattenableUtils::write(buffer, size, signalTime);
+ return NO_ERROR;
+ }
+
+ return NO_ERROR;
+}
+
+status_t FenceTime::Snapshot::unflatten(
+ void const*& buffer, size_t& size, int const*& fds, size_t& count) {
+ if (size < sizeof(state)) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, state);
+ switch (state) {
+ case State::EMPTY:
+ return NO_ERROR;
+ case State::FENCE:
+ fence = new Fence;
+ return fence->unflatten(buffer, size, fds, count);
+ case State::SIGNAL_TIME:
+ if (size < sizeof(signalTime)) {
+ return NO_MEMORY;
+ }
+ FlattenableUtils::read(buffer, size, signalTime);
+ return NO_ERROR;
+ }
+
+ return NO_ERROR;
+}
+
+// ============================================================================
+// FenceTimeline
+// ============================================================================
+void FenceTimeline::push(const std::shared_ptr<FenceTime>& fence) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ while (mQueue.size() >= MAX_ENTRIES) {
+ // This is a sanity check to make sure the queue doesn't grow unbounded.
+ // MAX_ENTRIES should be big enough not to trigger this path.
+ // In case this path is taken though, users of FenceTime must make sure
+ // not to rely solely on FenceTimeline to get the final timestamp and
+ // should eventually call Fence::getSignalTime on their own.
+ std::shared_ptr<FenceTime> front = mQueue.front().lock();
+ if (front) {
+ // Make a last ditch effort to get the signalTime here since
+ // we are removing it from the timeline.
+ front->getSignalTime();
+ }
+ mQueue.pop();
+ }
+ mQueue.push(fence);
+}
+
+void FenceTimeline::updateSignalTimes() {
+ while (!mQueue.empty()) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ std::shared_ptr<FenceTime> fence = mQueue.front().lock();
+ if (!fence) {
+ // The shared_ptr no longer exists and no one cares about the
+ // timestamp anymore.
+ mQueue.pop();
+ continue;
+ } else if (fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING) {
+ // The fence has signaled and we've removed the sp<Fence> ref.
+ mQueue.pop();
+ continue;
+ } else {
+ // The fence didn't signal yet. Break since the later ones
+ // shouldn't have signaled either.
+ break;
+ }
+ }
+}
+
+} // namespace android
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index c2fa43f..73e5e07 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -636,8 +636,9 @@
#define EGL_RENDERING_COMPLETE_TIME_ANDROID 0x314F
#define EGL_COMPOSITION_START_TIME_ANDROID 0x3430
#define EGL_COMPOSITION_FINISHED_TIME_ANDROID 0x3431
-#define EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3432
-#define EGL_READS_DONE_TIME_ANDROID 0x3433
+#define EGL_DISPLAY_PRESENT_TIME_ANDROID 0x3432
+#define EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3433
+#define EGL_READS_DONE_TIME_ANDROID 0x3434
#ifdef EGL_EGLEXT_PROTOTYPES
EGLAPI EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface, EGLint framesAgo, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values);
EGLAPI EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface, EGLint timestamp);
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 1acdeb08..3d3df76 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -1204,6 +1204,9 @@
egl_surface_t * const s = get_surface(surface);
if (attribute == EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID) {
+ if (!s->win.get()) {
+ setError(EGL_BAD_SURFACE, EGL_FALSE);
+ }
int err = native_window_set_auto_refresh(s->win.get(),
value ? true : false);
return (err == NO_ERROR) ? EGL_TRUE :
@@ -1212,8 +1215,13 @@
#if ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS
if (attribute == EGL_TIMESTAMPS_ANDROID) {
- s->enableTimestamps = value;
- return EGL_TRUE;
+ if (!s->win.get()) {
+ return setError(EGL_BAD_SURFACE, EGL_FALSE);
+ }
+ int err = native_window_enable_frame_timestamps(
+ s->win.get(), value ? true : false);
+ return (err == NO_ERROR) ? EGL_TRUE :
+ setError(EGL_BAD_SURFACE, EGL_FALSE);
}
#endif
@@ -2021,27 +2029,25 @@
const egl_display_ptr dp = validate_display(dpy);
if (!dp) {
- setError(EGL_BAD_DISPLAY, EGL_FALSE);
- return EGL_FALSE;
+ return setError(EGL_BAD_DISPLAY, EGL_FALSE);
}
SurfaceRef _s(dp.get(), surface);
if (!_s.get()) {
- setError(EGL_BAD_SURFACE, EGL_FALSE);
- return EGL_FALSE;
+ return setError(EGL_BAD_SURFACE, EGL_FALSE);
}
egl_surface_t const * const s = get_surface(surface);
- if (!s->enableTimestamps) {
- setError(EGL_BAD_SURFACE, EGL_FALSE);
- return EGL_FALSE;
+ if (!s->win.get()) {
+ return setError(EGL_BAD_SURFACE, EGL_FALSE);
}
nsecs_t* requestedPresentTime = nullptr;
nsecs_t* acquireTime = nullptr;
nsecs_t* refreshStartTime = nullptr;
nsecs_t* GLCompositionDoneTime = nullptr;
+ nsecs_t* displayPresentTime = nullptr;
nsecs_t* displayRetireTime = nullptr;
nsecs_t* releaseTime = nullptr;
@@ -2059,6 +2065,9 @@
case EGL_COMPOSITION_FINISHED_TIME_ANDROID:
GLCompositionDoneTime = &values[i];
break;
+ case EGL_DISPLAY_PRESENT_TIME_ANDROID:
+ displayPresentTime = &values[i];
+ break;
case EGL_DISPLAY_RETIRE_TIME_ANDROID:
displayRetireTime = &values[i];
break;
@@ -2066,21 +2075,29 @@
releaseTime = &values[i];
break;
default:
- setError(EGL_BAD_PARAMETER, EGL_FALSE);
- return EGL_FALSE;
+ return setError(EGL_BAD_PARAMETER, EGL_FALSE);
}
}
status_t ret = native_window_get_frame_timestamps(s->win.get(), framesAgo,
requestedPresentTime, acquireTime, refreshStartTime,
- GLCompositionDoneTime, displayRetireTime, releaseTime);
+ GLCompositionDoneTime, displayPresentTime, displayRetireTime,
+ releaseTime);
- if (ret != NO_ERROR) {
- setError(EGL_BAD_ACCESS, EGL_FALSE);
- return EGL_FALSE;
+ switch (ret) {
+ case NO_ERROR:
+ return EGL_TRUE;
+ case NAME_NOT_FOUND:
+ return setError(EGL_BAD_ACCESS, EGL_FALSE);
+ case INVALID_OPERATION:
+ return setError(EGL_BAD_SURFACE, EGL_FALSE);
+ case BAD_VALUE:
+ return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+ default:
+ // This should not happen. Return an error that is not in the spec
+ // so it's obvious something is very wrong.
+ return setError(EGL_NOT_INITIALIZED, EGL_FALSE);
}
-
- return EGL_TRUE;
}
EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface,
@@ -2090,14 +2107,19 @@
const egl_display_ptr dp = validate_display(dpy);
if (!dp) {
- setError(EGL_BAD_DISPLAY, EGL_FALSE);
- return EGL_FALSE;
+ return setError(EGL_BAD_DISPLAY, EGL_FALSE);
}
SurfaceRef _s(dp.get(), surface);
if (!_s.get()) {
- setError(EGL_BAD_SURFACE, EGL_FALSE);
- return EGL_FALSE;
+ 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 (timestamp) {
@@ -2106,9 +2128,20 @@
case EGL_RENDERING_COMPLETE_TIME_ANDROID:
case EGL_COMPOSITION_START_TIME_ANDROID:
case EGL_COMPOSITION_FINISHED_TIME_ANDROID:
- case EGL_DISPLAY_RETIRE_TIME_ANDROID:
case EGL_READS_DONE_TIME_ANDROID:
return EGL_TRUE;
+ case EGL_DISPLAY_PRESENT_TIME_ANDROID: {
+ int value = 0;
+ window->query(window,
+ NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &value);
+ return value == 0 ? EGL_FALSE : EGL_TRUE;
+ }
+ case EGL_DISPLAY_RETIRE_TIME_ANDROID: {
+ int value = 0;
+ window->query(window,
+ NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_RETIRE, &value);
+ return value == 0 ? EGL_FALSE : EGL_TRUE;
+ }
#endif
default:
return EGL_FALSE;
diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp
index 6a76737..7fc5609 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -68,7 +68,7 @@
EGLNativeWindowType win, EGLSurface surface,
egl_connection_t const* cnx) :
egl_object_t(dpy), surface(surface), config(config), win(win), cnx(cnx),
- enableTimestamps(false), connected(true)
+ connected(true)
{}
egl_surface_t::~egl_surface_t() {
diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h
index 3150ba6..8ceba1d 100644
--- a/opengl/libs/EGL/egl_object.h
+++ b/opengl/libs/EGL/egl_object.h
@@ -139,7 +139,6 @@
EGLConfig config;
sp<ANativeWindow> win;
egl_connection_t const* cnx;
- bool enableTimestamps;
private:
bool connected;
void disconnect();
diff --git a/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt b/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt
index 0882cef..b5b6eb5 100644
--- a/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt
+++ b/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt
@@ -38,8 +38,8 @@
and display of window surfaces.
Some examples of how this might be used:
- - The display retire time can be used to calculate end-to-end latency of
- the entire graphics pipeline.
+ - The display present or retire time can be used to calculate end-to-end
+ latency of the entire graphics pipeline.
- The queue time and rendering complete time can be used to determine
how long the application's rendering took to complete. Likewise, the
composition start time and finish time can be used to determine how
@@ -71,8 +71,9 @@
EGL_RENDERING_COMPLETE_TIME_ANDROID 0x314F
EGL_COMPOSITION_START_TIME_ANDROID 0x3430
EGL_COMPOSITION_FINISHED_TIME_ANDROID 0x3431
- EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3432
- EGL_READS_DONE_TIME_ANDROID 0x3433
+ EGL_DISPLAY_PRESENT_TIME_ANDROID 0x3432
+ EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3433
+ EGL_READS_DONE_TIME_ANDROID 0x3434
Add to the list of supported tokens for eglSurfaceAttrib in section 3.5.6
"Surface Attributes", page 43:
@@ -124,12 +125,14 @@
compositor's rendering work for this frame finished. This will be zero
if composition was handled by the display and the compositor didn't do
any rendering.
+ - EGL_DISPLAY_PRESENT_TIME_ANDROID - The time at which this frame
+ started to scan out on the physical display.
- EGL_DISPLAY_RETIRE_TIME_ANDROID - The time at which this frame was
replaced by the next frame on-screen.
- EGL_READS_DONE_TIME_ANDROID - The time at which all reads for the
purpose of display/composition were completed for this frame.
- Not all implementations may support all off the above timestamp queries. The
+ Not all implementations may support all of the above timestamp queries. The
function
EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface
@@ -148,4 +151,4 @@
#2 (Brian Anderson, July 22, 2016)
- Replace EGL_QUEUE_TIME_ANDROID with EGL_REQUESTED_PRESENT_TIME_ANDROID.
-
+ - Add DISPLAY_PRESENT_TIME_ANDROID.
diff --git a/opengl/specs/README b/opengl/specs/README
index 8414d05..1ee99fb 100644
--- a/opengl/specs/README
+++ b/opengl/specs/README
@@ -24,6 +24,7 @@
0x314F EGL_RENDERING_COMPLETE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
0x3430 EGL_COMPOSITION_START_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
0x3431 EGL_COMPOSITION_FINISHED_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
-0x3432 EGL_DISPLAY_RETIRE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
-0x3433 EGL_READS_DONE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
-0x3434 - 0x343F (unused)
+0x3432 EGL_DISPLAY_PRESENT_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3433 EGL_DISPLAY_RETIRE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3434 EGL_READS_DONE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3435 - 0x343F (unused)
diff --git a/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.java b/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.java
new file mode 100644
index 0000000..bc6740e
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.java
@@ -0,0 +1,10 @@
+ // C function EGLSurface eglCreatePixmapSurface ( EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, const EGLint *attrib_list )
+
+ @Deprecated
+ public static native EGLSurface eglCreatePixmapSurface(
+ EGLDisplay dpy,
+ EGLConfig config,
+ int pixmap,
+ int[] attrib_list,
+ int offset
+ );
\ No newline at end of file
diff --git a/opengl/tools/glgen/stubs/gles11/glGetBufferPointerv.java b/opengl/tools/glgen/stubs/gles11/glGetBufferPointerv.java
index c966e11..57338c7 100644
--- a/opengl/tools/glgen/stubs/gles11/glGetBufferPointerv.java
+++ b/opengl/tools/glgen/stubs/gles11/glGetBufferPointerv.java
@@ -1,5 +1,9 @@
// C function void glGetBufferPointerv ( GLenum target, GLenum pname, GLvoid** params )
+ /**
+ * The {@link java.nio.Buffer} instance returned by this method is guaranteed
+ * to be an instance of {@link java.nio.ByteBuffer}.
+ */
public static native java.nio.Buffer glGetBufferPointerv(
int target,
int pname
diff --git a/opengl/tools/glgen/stubs/gles11/glMapBufferRange.java b/opengl/tools/glgen/stubs/gles11/glMapBufferRange.java
index 482ea99..7b1966b 100644
--- a/opengl/tools/glgen/stubs/gles11/glMapBufferRange.java
+++ b/opengl/tools/glgen/stubs/gles11/glMapBufferRange.java
@@ -1,5 +1,9 @@
// C function GLvoid * glMapBufferRange ( GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access )
+ /**
+ * The {@link java.nio.Buffer} instance returned by this method is guaranteed
+ * to be an instance of {@link java.nio.ByteBuffer}.
+ */
public static native java.nio.Buffer glMapBufferRange(
int target,
int offset,
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index 4369fd2..20fe0fd 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -10,7 +10,6 @@
DispSync.cpp \
EventControlThread.cpp \
EventThread.cpp \
- FenceTracker.cpp \
FrameTracker.cpp \
GpuService.cpp \
Layer.cpp \
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index 18c7945..61c231d 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -205,7 +205,7 @@
void FramebufferSurface::onFrameCommitted() {
#ifdef USE_HWC2
if (mHasPendingRelease) {
- sp<Fence> fence = mHwc.getRetireFence(mDisplayType);
+ sp<Fence> fence = mHwc.getPresentFence(mDisplayType);
if (fence->isValid()) {
status_t result = addReleaseFence(mPreviousBufferSlot,
mPreviousBuffer, fence);
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 31af8a1..dd909aa 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -924,21 +924,21 @@
return Error::None;
}
-Error Display::present(sp<Fence>* outRetireFence)
+Error Display::present(sp<Fence>* outPresentFence)
{
- int32_t retireFenceFd = 0;
+ int32_t presentFenceFd = 0;
#ifdef BYPASS_IHWC
int32_t intError = mDevice.mPresentDisplay(mDevice.mHwcDevice, mId,
- &retireFenceFd);
+ &presentFenceFd);
#else
- auto intError = mDevice.mComposer->presentDisplay(mId, retireFenceFd);
+ auto intError = mDevice.mComposer->presentDisplay(mId, presentFenceFd);
#endif
auto error = static_cast<Error>(intError);
if (error != Error::None) {
return error;
}
- *outRetireFence = new Fence(retireFenceFd);
+ *outPresentFence = new Fence(presentFenceFd);
return Error::None;
}
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 1c709b2..33fb8cb 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -313,7 +313,7 @@
std::unordered_map<std::shared_ptr<Layer>,
android::sp<android::Fence>>* outFences) const;
[[clang::warn_unused_result]] Error present(
- android::sp<android::Fence>* outRetireFence);
+ android::sp<android::Fence>* outPresentFence);
[[clang::warn_unused_result]] Error setActiveConfig(
const std::shared_ptr<const Config>& config);
[[clang::warn_unused_result]] Error setClientTarget(
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 82a900c..c82b0c4 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -309,22 +309,22 @@
return layer;
}
-nsecs_t HWComposer::getRefreshTimestamp(int32_t disp) const {
+nsecs_t HWComposer::getRefreshTimestamp(int32_t displayId) const {
// this returns the last refresh timestamp.
// if the last one is not available, we estimate it based on
// the refresh period and whatever closest timestamp we have.
Mutex::Autolock _l(mLock);
nsecs_t now = systemTime(CLOCK_MONOTONIC);
- auto vsyncPeriod = getActiveConfig(disp)->getVsyncPeriod();
- return now - ((now - mLastHwVSync[disp]) % vsyncPeriod);
+ auto vsyncPeriod = getActiveConfig(displayId)->getVsyncPeriod();
+ return now - ((now - mLastHwVSync[displayId]) % vsyncPeriod);
}
-bool HWComposer::isConnected(int32_t disp) const {
- if (!isValidDisplay(disp)) {
- ALOGE("isConnected: Attempted to access invalid display %d", disp);
+bool HWComposer::isConnected(int32_t displayId) const {
+ if (!isValidDisplay(displayId)) {
+ ALOGE("isConnected: Attempted to access invalid display %d", displayId);
return false;
}
- return mDisplayData[disp].hwcDisplay->isConnected();
+ return mDisplayData[displayId].hwcDisplay->isConnected();
}
std::vector<std::shared_ptr<const HWC2::Display::Config>>
@@ -408,14 +408,15 @@
}
-void HWComposer::setVsyncEnabled(int32_t disp, HWC2::Vsync enabled) {
- if (disp < 0 || disp >= HWC_DISPLAY_VIRTUAL) {
- ALOGD("setVsyncEnabled: Ignoring for virtual display %d", disp);
+void HWComposer::setVsyncEnabled(int32_t displayId, HWC2::Vsync enabled) {
+ if (displayId < 0 || displayId >= HWC_DISPLAY_VIRTUAL) {
+ ALOGD("setVsyncEnabled: Ignoring for virtual display %d", displayId);
return;
}
- if (!isValidDisplay(disp)) {
- ALOGE("setVsyncEnabled: Attempted to access invalid display %d", disp);
+ if (!isValidDisplay(displayId)) {
+ ALOGE("setVsyncEnabled: Attempted to access invalid display %d",
+ displayId);
return;
}
@@ -424,7 +425,7 @@
// that even if HWC blocks (which it shouldn't), it won't
// affect other threads.
Mutex::Autolock _l(mVsyncLock);
- auto& displayData = mDisplayData[disp];
+ auto& displayData = mDisplayData[displayId];
if (enabled != displayData.vsyncEnabled) {
ATRACE_CALL();
auto error = displayData.hwcDisplay->setVsyncEnabled(enabled);
@@ -432,12 +433,12 @@
displayData.vsyncEnabled = enabled;
char tag[16];
- snprintf(tag, sizeof(tag), "HW_VSYNC_ON_%1u", disp);
+ snprintf(tag, sizeof(tag), "HW_VSYNC_ON_%1u", displayId);
ATRACE_INT(tag, enabled == HWC2::Vsync::Enable ? 1 : 0);
} else {
ALOGE("setVsyncEnabled: Failed to set vsync to %s on %d/%" PRIu64
- ": %s (%d)", to_string(enabled).c_str(), disp,
- mDisplayData[disp].hwcDisplay->getId(),
+ ": %s (%d)", to_string(enabled).c_str(), displayId,
+ mDisplayData[displayId].hwcDisplay->getId(),
to_string(error).c_str(), static_cast<int32_t>(error));
}
}
@@ -593,12 +594,16 @@
return mDisplayData[displayId].hasClientComposition;
}
-sp<Fence> HWComposer::getRetireFence(int32_t displayId) const {
+sp<Fence> HWComposer::getPresentFence(int32_t displayId) const {
if (!isValidDisplay(displayId)) {
- ALOGE("getRetireFence failed for invalid display %d", displayId);
+ ALOGE("getPresentFence failed for invalid display %d", displayId);
return Fence::NO_FENCE;
}
- return mDisplayData[displayId].lastRetireFence;
+ return mDisplayData[displayId].lastPresentFence;
+}
+
+bool HWComposer::retireFenceRepresentsStartOfScanout() const {
+ return mAdapter ? false : true;
}
sp<Fence> HWComposer::getLayerReleaseFence(int32_t displayId,
@@ -615,7 +620,7 @@
return displayFences[layer];
}
-status_t HWComposer::commit(int32_t displayId) {
+status_t HWComposer::presentAndGetReleaseFences(int32_t displayId) {
ATRACE_CALL();
if (!isValidDisplay(displayId)) {
@@ -624,17 +629,18 @@
auto& displayData = mDisplayData[displayId];
auto& hwcDisplay = displayData.hwcDisplay;
- auto error = hwcDisplay->present(&displayData.lastRetireFence);
+ auto error = hwcDisplay->present(&displayData.lastPresentFence);
if (error != HWC2::Error::None) {
- ALOGE("commit: present failed for display %d: %s (%d)", displayId,
- to_string(error).c_str(), static_cast<int32_t>(error));
+ ALOGE("presentAndGetReleaseFences: failed for display %d: %s (%d)",
+ displayId, to_string(error).c_str(), static_cast<int32_t>(error));
return UNKNOWN_ERROR;
}
std::unordered_map<std::shared_ptr<HWC2::Layer>, sp<Fence>> releaseFences;
error = hwcDisplay->getReleaseFences(&releaseFences);
if (error != HWC2::Error::None) {
- ALOGE("commit: Failed to get release fences for display %d: %s (%d)",
+ ALOGE("presentAndGetReleaseFences: Failed to get release fences "
+ "for display %d: %s (%d)",
displayId, to_string(error).c_str(),
static_cast<int32_t>(error));
return UNKNOWN_ERROR;
@@ -873,7 +879,7 @@
: hasClientComposition(false),
hasDeviceComposition(false),
hwcDisplay(),
- lastRetireFence(Fence::NO_FENCE),
+ lastPresentFence(Fence::NO_FENCE),
outbufHandle(nullptr),
outbufAcquireFence(Fence::NO_FENCE),
vsyncEnabled(HWC2::Vsync::Disable) {
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 41671f6..e63bdd4 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -97,8 +97,8 @@
status_t setClientTarget(int32_t displayId, const sp<Fence>& acquireFence,
const sp<GraphicBuffer>& target, android_dataspace_t dataspace);
- // Finalize the layers and present them
- status_t commit(int32_t displayId);
+ // Present layers to the display and read releaseFences.
+ status_t presentAndGetReleaseFences(int32_t displayId);
// set power mode
status_t setPowerMode(int32_t displayId, int mode);
@@ -118,9 +118,13 @@
// does this display have layers handled by GLES
bool hasClientComposition(int32_t displayId) const;
- // get the retire fence for the previous frame (i.e., corresponding to the
- // last call to presentDisplay
- sp<Fence> getRetireFence(int32_t displayId) const;
+ // get the present fence received from the last call to present.
+ sp<Fence> getPresentFence(int32_t displayId) const;
+
+ // Returns true if the retire fence represents the start of the display
+ // controller's scan out. This should be true for all HWC2 implementations,
+ // except for the wrapper around HWC1 implementations.
+ bool retireFenceRepresentsStartOfScanout() const;
// Get last release fence for the given layer
sp<Fence> getLayerReleaseFence(int32_t displayId,
@@ -140,12 +144,12 @@
// Events handling ---------------------------------------------------------
- void setVsyncEnabled(int32_t disp, HWC2::Vsync enabled);
+ void setVsyncEnabled(int32_t displayId, HWC2::Vsync enabled);
// Query display parameters. Pass in a display index (e.g.
// HWC_DISPLAY_PRIMARY).
- nsecs_t getRefreshTimestamp(int32_t disp) const;
- bool isConnected(int32_t disp) const;
+ nsecs_t getRefreshTimestamp(int32_t displayId) const;
+ bool isConnected(int32_t displayId) const;
// Non-const because it can update configMap inside of mDisplayData
std::vector<std::shared_ptr<const HWC2::Display::Config>>
@@ -186,7 +190,7 @@
bool hasDeviceComposition;
std::shared_ptr<HWC2::Display> hwcDisplay;
HWC2::DisplayRequest displayRequests;
- sp<Fence> lastRetireFence; // signals when the last set op retires
+ sp<Fence> lastPresentFence; // signals when the last set op retires
std::unordered_map<std::shared_ptr<HWC2::Layer>, sp<Fence>>
releaseFences;
buffer_handle_t outbufHandle;
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 2190466..0511df2 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -241,7 +241,7 @@
mDbgState = DBG_STATE_IDLE;
#ifdef USE_HWC2
- sp<Fence> retireFence = mHwc.getRetireFence(mDisplayId);
+ sp<Fence> retireFence = mHwc.getPresentFence(mDisplayId);
#else
sp<Fence> fbFence = mHwc.getAndResetReleaseFence(mDisplayId);
#endif
@@ -303,13 +303,8 @@
}
void VirtualDisplaySurface::resizeBuffers(const uint32_t w, const uint32_t h) {
- uint32_t tmpW, tmpH, transformHint, numPendingBuffers;
- uint64_t nextFrameNumber;
- mQueueBufferOutput.deflate(&tmpW, &tmpH, &transformHint, &numPendingBuffers,
- &nextFrameNumber);
- mQueueBufferOutput.inflate(w, h, transformHint, numPendingBuffers,
- nextFrameNumber);
-
+ mQueueBufferOutput.width = w;
+ mQueueBufferOutput.height = h;
mSinkBufferWidth = w;
mSinkBufferHeight = h;
}
@@ -345,7 +340,7 @@
LOG_FATAL_IF(mDisplayId < 0, "mDisplayId=%d but should not be < 0.", mDisplayId);
status_t result = mSource[source]->dequeueBuffer(sslot, fence,
- mSinkBufferWidth, mSinkBufferHeight, format, usage);
+ mSinkBufferWidth, mSinkBufferHeight, format, usage, nullptr);
if (result < 0)
return result;
int pslot = mapSource2ProducerSlot(source, *sslot);
@@ -384,9 +379,12 @@
}
status_t VirtualDisplaySurface::dequeueBuffer(int* pslot, sp<Fence>* fence,
- uint32_t w, uint32_t h, PixelFormat format, uint32_t usage) {
- if (mDisplayId < 0)
- return mSource[SOURCE_SINK]->dequeueBuffer(pslot, fence, w, h, format, usage);
+ uint32_t w, uint32_t h, PixelFormat format, uint32_t usage,
+ FrameEventHistoryDelta* outTimestamps) {
+ if (mDisplayId < 0) {
+ return mSource[SOURCE_SINK]->dequeueBuffer(
+ pslot, fence, w, h, format, usage, outTimestamps);
+ }
VDS_LOGW_IF(mDbgState != DBG_STATE_PREPARED,
"Unexpected dequeueBuffer() in %s state", dbgStateStr());
@@ -618,10 +616,8 @@
void VirtualDisplaySurface::updateQueueBufferOutput(
const QueueBufferOutput& qbo) {
- uint32_t w, h, transformHint, numPendingBuffers;
- uint64_t nextFrameNumber;
- qbo.deflate(&w, &h, &transformHint, &numPendingBuffers, &nextFrameNumber);
- mQueueBufferOutput.inflate(w, h, 0, numPendingBuffers, nextFrameNumber);
+ mQueueBufferOutput = qbo;
+ mQueueBufferOutput.transformHint = 0;
}
void VirtualDisplaySurface::resetPerFrameState() {
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index 70f717f..b435bf5 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -104,7 +104,8 @@
virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers);
virtual status_t setAsyncMode(bool async);
virtual status_t dequeueBuffer(int* pslot, sp<Fence>* fence, uint32_t w,
- uint32_t h, PixelFormat format, uint32_t usage);
+ uint32_t h, PixelFormat format, uint32_t usage,
+ FrameEventHistoryDelta *outTimestamps);
virtual status_t detachBuffer(int slot);
virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer,
sp<Fence>* outFence);
diff --git a/services/surfaceflinger/FenceTracker.cpp b/services/surfaceflinger/FenceTracker.cpp
deleted file mode 100644
index 276890d..0000000
--- a/services/surfaceflinger/FenceTracker.cpp
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include <inttypes.h>
-#include "FenceTracker.h"
-#include "Layer.h"
-#include <utils/Trace.h>
-
-namespace android {
-
-FenceTracker::FenceTracker() :
- mFrameCounter(0),
- mOffset(0),
- mFrames(),
- mMutex() {
-}
-
-void FenceTracker::dump(String8* outString) {
- Mutex::Autolock lock(mMutex);
- checkFencesForCompletion();
-
- for (size_t i = 0; i < MAX_FRAME_HISTORY; i++) {
- int index = (mOffset + i) % MAX_FRAME_HISTORY;
- const FrameRecord& frame = mFrames[index];
-
- outString->appendFormat("Frame %" PRIu64 "\n", frame.frameId);
- outString->appendFormat("- Refresh start\t%" PRId64 "\n",
- frame.refreshStartTime);
-
- if (frame.glesCompositionDoneTime) {
- outString->appendFormat("- GLES done\t%" PRId64 "\n",
- frame.glesCompositionDoneTime);
- } else if (frame.glesCompositionDoneFence != Fence::NO_FENCE) {
- outString->append("- GLES done\tNot signaled\n");
- }
- if (frame.retireTime) {
- outString->appendFormat("- Retire\t%" PRId64 "\n",
- frame.retireTime);
- } else {
- outString->append("- Retire\tNot signaled\n");
- }
- for (const auto& kv : frame.layers) {
- const LayerRecord& layer = kv.second;
- outString->appendFormat("-- %s\n", layer.name.string());
- outString->appendFormat("---- Frame # %" PRIu64 " (%s)\n",
- layer.frameNumber,
- layer.isGlesComposition ? "GLES" : "HWC");
- outString->appendFormat("---- Req.Present.\t%" PRId64 "\n",
- layer.requestedPresentTime);
- if (layer.acquireTime) {
- outString->appendFormat("---- Acquire\t%" PRId64 "\n",
- layer.acquireTime);
- } else {
- outString->append("---- Acquire\tNot signaled\n");
- }
- if (layer.releaseTime) {
- outString->appendFormat("---- Release\t%" PRId64 "\n",
- layer.releaseTime);
- } else {
- outString->append("---- Release\tNot signaled\n");
- }
- }
- }
-}
-
-static inline bool isValidTimestamp(nsecs_t time) {
- return time > 0 && time < INT64_MAX;
-}
-
-void FenceTracker::checkFencesForCompletion() {
- ATRACE_CALL();
- for (auto& frame : mFrames) {
- if (frame.retireFence != Fence::NO_FENCE) {
- nsecs_t time = frame.retireFence->getSignalTime();
- if (isValidTimestamp(time)) {
- frame.retireTime = time;
- frame.retireFence = Fence::NO_FENCE;
- }
- }
- if (frame.glesCompositionDoneFence != Fence::NO_FENCE) {
- nsecs_t time = frame.glesCompositionDoneFence->getSignalTime();
- if (isValidTimestamp(time)) {
- frame.glesCompositionDoneTime = time;
- frame.glesCompositionDoneFence = Fence::NO_FENCE;
- }
- }
- for (auto& kv : frame.layers) {
- LayerRecord& layer = kv.second;
- if (layer.acquireFence != Fence::NO_FENCE) {
- nsecs_t time = layer.acquireFence->getSignalTime();
- if (isValidTimestamp(time)) {
- layer.acquireTime = time;
- layer.acquireFence = Fence::NO_FENCE;
- }
- }
- if (layer.releaseFence != Fence::NO_FENCE) {
- nsecs_t time = layer.releaseFence->getSignalTime();
- if (isValidTimestamp(time)) {
- layer.releaseTime = time;
- layer.releaseFence = Fence::NO_FENCE;
- }
- }
- }
- }
-}
-
-void FenceTracker::addFrame(nsecs_t refreshStartTime, sp<Fence> retireFence,
- const Vector<sp<Layer>>& layers, sp<Fence> glDoneFence) {
- ATRACE_CALL();
- Mutex::Autolock lock(mMutex);
- FrameRecord& frame = mFrames[mOffset];
- FrameRecord& prevFrame = mFrames[(mOffset + MAX_FRAME_HISTORY - 1) %
- MAX_FRAME_HISTORY];
- frame.layers.clear();
-
- bool wasGlesCompositionDone = false;
- const size_t count = layers.size();
- for (size_t i = 0; i < count; i++) {
- String8 name;
- uint64_t frameNumber;
- bool glesComposition;
- nsecs_t requestedPresentTime;
- sp<Fence> acquireFence;
- sp<Fence> prevReleaseFence;
- int32_t layerId = layers[i]->getSequence();
-
- layers[i]->getFenceData(&name, &frameNumber, &glesComposition,
- &requestedPresentTime, &acquireFence, &prevReleaseFence);
-#ifdef USE_HWC2
- if (glesComposition) {
- frame.layers.emplace(std::piecewise_construct,
- std::forward_as_tuple(layerId),
- std::forward_as_tuple(name, frameNumber, glesComposition,
- requestedPresentTime, 0, 0, acquireFence,
- prevReleaseFence));
- wasGlesCompositionDone = true;
- } else {
- frame.layers.emplace(std::piecewise_construct,
- std::forward_as_tuple(layerId),
- std::forward_as_tuple(name, frameNumber, glesComposition,
- requestedPresentTime, 0, 0, acquireFence, Fence::NO_FENCE));
- auto prevLayer = prevFrame.layers.find(layerId);
- if (prevLayer != prevFrame.layers.end()) {
- prevLayer->second.releaseFence = prevReleaseFence;
- }
- }
-#else
- frame.layers.emplace(std::piecewise_construct,
- std::forward_as_tuple(layerId),
- std::forward_as_tuple(name, frameNumber, glesComposition,
- requestedPresentTime, 0, 0, acquireFence,
- glesComposition ? Fence::NO_FENCE : prevReleaseFence));
- if (glesComposition) {
- wasGlesCompositionDone = true;
- }
-#endif
- frame.layers.emplace(std::piecewise_construct,
- std::forward_as_tuple(layerId),
- std::forward_as_tuple(name, frameNumber, glesComposition,
- requestedPresentTime, 0, 0, acquireFence, prevReleaseFence));
- }
-
- frame.frameId = mFrameCounter;
- frame.refreshStartTime = refreshStartTime;
- frame.retireTime = 0;
- frame.glesCompositionDoneTime = 0;
- prevFrame.retireFence = retireFence;
- frame.retireFence = Fence::NO_FENCE;
- frame.glesCompositionDoneFence = wasGlesCompositionDone ? glDoneFence :
- Fence::NO_FENCE;
-
- mOffset = (mOffset + 1) % MAX_FRAME_HISTORY;
- mFrameCounter++;
-}
-
-bool FenceTracker::getFrameTimestamps(const Layer& layer,
- uint64_t frameNumber, FrameTimestamps* outTimestamps) {
- Mutex::Autolock lock(mMutex);
- checkFencesForCompletion();
- int32_t layerId = layer.getSequence();
-
- size_t i = 0;
- for (; i < MAX_FRAME_HISTORY; i++) {
- if (mFrames[i].layers.count(layerId) &&
- mFrames[i].layers[layerId].frameNumber == frameNumber) {
- break;
- }
- }
- if (i == MAX_FRAME_HISTORY) {
- return false;
- }
-
- const FrameRecord& frameRecord = mFrames[i];
- const LayerRecord& layerRecord = mFrames[i].layers[layerId];
- outTimestamps->frameNumber = frameNumber;
- outTimestamps->requestedPresentTime = layerRecord.requestedPresentTime;
- outTimestamps->acquireTime = layerRecord.acquireTime;
- outTimestamps->refreshStartTime = frameRecord.refreshStartTime;
- outTimestamps->glCompositionDoneTime = frameRecord.glesCompositionDoneTime;
- outTimestamps->displayRetireTime = frameRecord.retireTime;
- outTimestamps->releaseTime = layerRecord.releaseTime;
- return true;
-}
-
-} // namespace android
diff --git a/services/surfaceflinger/FenceTracker.h b/services/surfaceflinger/FenceTracker.h
deleted file mode 100644
index 385c970..0000000
--- a/services/surfaceflinger/FenceTracker.h
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright 2016 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.
- */
-
-#ifndef ANDROID_FENCETRACKER_H
-#define ANDROID_FENCETRACKER_H
-
-#include <ui/Fence.h>
-#include <utils/Mutex.h>
-#include <utils/RefBase.h>
-#include <utils/String8.h>
-#include <utils/Timers.h>
-#include <utils/Vector.h>
-
-#include <unordered_map>
-
-namespace android {
-
-class Layer;
-struct FrameTimestamps;
-/*
- * Keeps a circular buffer of fence/timestamp data for the last N frames in
- * SurfaceFlinger. Gets timestamps for fences after they have signaled.
- */
-class FenceTracker {
-public:
- FenceTracker();
- void dump(String8* outString);
- void addFrame(nsecs_t refreshStartTime, sp<Fence> retireFence,
- const Vector<sp<Layer>>& layers, sp<Fence> glDoneFence);
- bool getFrameTimestamps(const Layer& layer, uint64_t frameNumber,
- FrameTimestamps* outTimestamps);
-
-protected:
- static constexpr size_t MAX_FRAME_HISTORY = 8;
-
- struct LayerRecord {
- String8 name; // layer name
- uint64_t frameNumber; // frame number for this layer
- bool isGlesComposition; // was GLES composition used for this layer?
- // time the producer requested this frame be presented
- nsecs_t requestedPresentTime;
- nsecs_t acquireTime; // timestamp from the acquire fence
- nsecs_t releaseTime; // timestamp from the release fence
- sp<Fence> acquireFence; // acquire fence
- sp<Fence> releaseFence; // release fence
-
- LayerRecord(const String8& name, uint64_t frameNumber,
- bool isGlesComposition, nsecs_t requestedPresentTime,
- nsecs_t acquireTime, nsecs_t releaseTime,
- sp<Fence> acquireFence, sp<Fence> releaseFence) :
- name(name), frameNumber(frameNumber),
- isGlesComposition(isGlesComposition),
- requestedPresentTime(requestedPresentTime),
- acquireTime(acquireTime), releaseTime(releaseTime),
- acquireFence(acquireFence), releaseFence(releaseFence) {};
- LayerRecord() : name("uninitialized"), frameNumber(0),
- isGlesComposition(false), requestedPresentTime(0),
- acquireTime(0), releaseTime(0), acquireFence(Fence::NO_FENCE),
- releaseFence(Fence::NO_FENCE) {};
- };
-
- struct FrameRecord {
- // global SurfaceFlinger frame counter
- uint64_t frameId;
- // layer data for this frame
- std::unordered_map<int32_t, LayerRecord> layers;
- // timestamp for when SurfaceFlinger::handleMessageRefresh() was called
- nsecs_t refreshStartTime;
- // timestamp from the retire fence
- nsecs_t retireTime;
- // timestamp from the GLES composition completion fence
- nsecs_t glesCompositionDoneTime;
- // primary display retire fence for this frame
- sp<Fence> retireFence;
- // if GLES composition was done, the fence for its completion
- sp<Fence> glesCompositionDoneFence;
-
- FrameRecord() : frameId(0), layers(), refreshStartTime(0),
- retireTime(0), glesCompositionDoneTime(0),
- retireFence(Fence::NO_FENCE),
- glesCompositionDoneFence(Fence::NO_FENCE) {}
- };
-
- uint64_t mFrameCounter;
- uint32_t mOffset;
- FrameRecord mFrames[MAX_FRAME_HISTORY];
- Mutex mMutex;
-
- void checkFencesForCompletion();
-};
-
-}
-
-#endif // ANDROID_FRAMETRACKER_H
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 20c0261..3b88a6e 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -80,7 +80,9 @@
mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
mOverrideScalingMode(-1),
mCurrentOpacity(true),
+ mBufferLatched(false),
mCurrentFrameNumber(0),
+ mPreviousFrameNumber(-1U),
mRefreshPending(false),
mFrameLatencyNeeded(false),
mFiltering(false),
@@ -1729,14 +1731,53 @@
return isDue || !isPlausible;
}
-bool Layer::onPreComposition() {
+bool Layer::onPreComposition(nsecs_t refreshStartTime) {
+ if (mBufferLatched) {
+ Mutex::Autolock lock(mFrameEventHistoryMutex);
+ mFrameEventHistory.addPreComposition(mCurrentFrameNumber, refreshStartTime);
+ }
mRefreshPending = false;
return mQueuedFrames > 0 || mSidebandStreamChanged || mAutoRefresh;
}
-bool Layer::onPostComposition() {
+bool Layer::onPostComposition(sp<Fence> glDoneFence) {
+ // mFrameLatencyNeeded is true when a new frame was latched for the
+ // composition.
bool frameLatencyNeeded = mFrameLatencyNeeded;
if (mFrameLatencyNeeded) {
+ const HWComposer& hwc = mFlinger->getHwComposer();
+#ifdef USE_HWC2
+ sp<Fence> retireFence = Fence::NO_FENCE;
+ sp<Fence> presentFence = Fence::NO_FENCE;
+ sp<Fence> presentOrRetireFence = Fence::NO_FENCE;
+ if (hwc.retireFenceRepresentsStartOfScanout()) {
+ presentFence = hwc.getPresentFence(HWC_DISPLAY_PRIMARY);
+ presentOrRetireFence = presentFence;
+ } else {
+ retireFence = hwc.getPresentFence(HWC_DISPLAY_PRIMARY);
+ presentOrRetireFence = retireFence;
+ }
+ bool wasGpuComposited = mHwcLayers.count(HWC_DISPLAY_PRIMARY) ?
+ mHwcLayers.at(HWC_DISPLAY_PRIMARY).compositionType ==
+ HWC2::Composition::Client : true;
+#else
+ sp<Fence> retireFence = hwc.getDisplayFence(HWC_DISPLAY_PRIMARY);
+ sp<Fence> presentFence = Fence::NO_FENCE;
+ sp<Fence> presentOrRetireFence = retireFence;
+ bool wasGpuComposited = mIsGlesComposition;
+#endif
+
+ // Update mFrameEventHistory.
+ {
+ Mutex::Autolock lock(mFrameEventHistoryMutex);
+ mFrameEventHistory.addPostComposition(mCurrentFrameNumber,
+ wasGpuComposited ? glDoneFence : Fence::NO_FENCE,
+ presentFence);
+ mFrameEventHistory.addRetire(mPreviousFrameNumber,
+ retireFence);
+ }
+
+ // Update mFrameTracker.
nsecs_t desiredPresentTime = mSurfaceFlingerConsumer->getTimestamp();
mFrameTracker.setDesiredPresentTime(desiredPresentTime);
@@ -1749,14 +1790,8 @@
mFrameTracker.setFrameReadyTime(desiredPresentTime);
}
- const HWComposer& hwc = mFlinger->getHwComposer();
-#ifdef USE_HWC2
- sp<Fence> presentFence = hwc.getRetireFence(HWC_DISPLAY_PRIMARY);
-#else
- sp<Fence> presentFence = hwc.getDisplayFence(HWC_DISPLAY_PRIMARY);
-#endif
- if (presentFence->isValid()) {
- mFrameTracker.setActualPresentFence(presentFence);
+ if (presentOrRetireFence->isValid()) {
+ mFrameTracker.setActualPresentFence(presentOrRetireFence);
} else {
// The HWC doesn't support present fences, so use the refresh
// timestamp instead.
@@ -1773,6 +1808,9 @@
#ifdef USE_HWC2
void Layer::releasePendingBuffer() {
mSurfaceFlingerConsumer->releasePendingBuffer();
+ Mutex::Autolock lock(mFrameEventHistoryMutex);
+ mFrameEventHistory.addRelease(mPreviousFrameNumber,
+ mSurfaceFlingerConsumer->getPrevFinalReleaseFence());
}
#endif
@@ -1813,7 +1851,7 @@
return !matchingFramesFound || allTransactionsApplied;
}
-Region Layer::latchBuffer(bool& recomputeVisibleRegions)
+Region Layer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime)
{
ATRACE_CALL();
@@ -1937,6 +1975,19 @@
return outDirtyRegion;
}
+ mBufferLatched = true;
+ mPreviousFrameNumber = mCurrentFrameNumber;
+ mCurrentFrameNumber = mSurfaceFlingerConsumer->getFrameNumber();
+
+ {
+ Mutex::Autolock lock(mFrameEventHistoryMutex);
+ mFrameEventHistory.addLatch(mCurrentFrameNumber, latchTime);
+#ifndef USE_HWC2
+ mFrameEventHistory.addRelease(mPreviousFrameNumber,
+ mSurfaceFlingerConsumer->getPrevFinalReleaseFence());
+#endif
+ }
+
mRefreshPending = true;
mFrameLatencyNeeded = true;
if (oldActiveBuffer == NULL) {
@@ -1972,8 +2023,6 @@
recomputeVisibleRegions = true;
}
- mCurrentFrameNumber = mSurfaceFlingerConsumer->getFrameNumber();
-
// Remove any sync points corresponding to the buffer which was just
// latched
{
@@ -2158,22 +2207,24 @@
mFrameTracker.getStats(outStats);
}
-void Layer::getFenceData(String8* outName, uint64_t* outFrameNumber,
- bool* outIsGlesComposition, nsecs_t* outRequestedPresentTime,
- sp<Fence>* outAcquireFence, sp<Fence>* outPrevReleaseFence) const {
- *outName = mName;
- *outFrameNumber = mSurfaceFlingerConsumer->getFrameNumber();
+void Layer::dumpFrameEvents(String8& result) {
+ result.appendFormat("- Layer %s (%s, %p)\n",
+ getName().string(), getTypeId(), this);
+ Mutex::Autolock lock(mFrameEventHistoryMutex);
+ mFrameEventHistory.checkFencesForCompletion();
+ mFrameEventHistory.dump(result);
+}
-#ifdef USE_HWC2
- *outIsGlesComposition = mHwcLayers.count(HWC_DISPLAY_PRIMARY) ?
- mHwcLayers.at(HWC_DISPLAY_PRIMARY).compositionType ==
- HWC2::Composition::Client : true;
-#else
- *outIsGlesComposition = mIsGlesComposition;
-#endif
- *outRequestedPresentTime = mSurfaceFlingerConsumer->getTimestamp();
- *outAcquireFence = mSurfaceFlingerConsumer->getCurrentFence();
- *outPrevReleaseFence = mSurfaceFlingerConsumer->getPrevReleaseFence();
+void Layer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
+ FrameEventHistoryDelta *outDelta) {
+ Mutex::Autolock lock(mFrameEventHistoryMutex);
+ if (newTimestamps) {
+ mFrameEventHistory.addQueue(*newTimestamps);
+ }
+
+ if (outDelta) {
+ mFrameEventHistory.getAndResetDelta(outDelta);
+ }
}
std::vector<OccupancyTracker::Segment> Layer::getOccupancyHistory(
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 5aba69b..b4d8685 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -270,13 +270,13 @@
* called before composition.
* returns true if the layer has pending updates.
*/
- bool onPreComposition();
+ bool onPreComposition(nsecs_t refreshStartTime);
/*
* called after composition.
* returns true if the layer latched a new buffer this frame.
*/
- bool onPostComposition();
+ bool onPostComposition(sp<Fence> glCompositionDoneFence);
#ifdef USE_HWC2
// If a buffer was replaced this frame, release the former buffer
@@ -323,7 +323,7 @@
* operation, so this should be set only if needed). Typically this is used
* to figure out if the content or size of a surface has changed.
*/
- Region latchBuffer(bool& recomputeVisibleRegions);
+ Region latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime);
bool isPotentialCursor() const { return mPotentialCursor;}
@@ -402,20 +402,15 @@
void miniDump(String8& result, int32_t hwcId) const;
#endif
void dumpFrameStats(String8& result) const;
+ void dumpFrameEvents(String8& result);
void clearFrameStats();
void logFrameStats();
void getFrameStats(FrameStats* outStats) const;
- void getFenceData(String8* outName, uint64_t* outFrameNumber,
- bool* outIsGlesComposition, nsecs_t* outRequestedPresentTime,
- sp<Fence>* outAcquireFence, sp<Fence>* outPrevReleaseFence) const;
-
std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool forceFlush);
- bool getFrameTimestamps(uint64_t frameNumber,
- FrameTimestamps* outTimestamps) const {
- return mFlinger->getFrameTimestamps(*this, frameNumber, outTimestamps);
- }
+ void addAndGetFrameTimestamps(const NewFrameEventsEntry* newEntry,
+ FrameEventHistoryDelta* outDelta);
bool getTransformToDisplayInverse() const;
@@ -583,8 +578,15 @@
// thread-safe
volatile int32_t mQueuedFrames;
volatile int32_t mSidebandStreamChanged; // used like an atomic boolean
+
+ // Timestamp history for UIAutomation. Thread safe.
FrameTracker mFrameTracker;
+ // Timestamp history for the consumer to query.
+ // Accessed by both consumer and producer on main and binder threads.
+ Mutex mFrameEventHistoryMutex;
+ ConsumerFrameEventHistory mFrameEventHistory;
+
// main thread
sp<GraphicBuffer> mActiveBuffer;
sp<NativeHandle> mSidebandStream;
@@ -594,7 +596,9 @@
// We encode unset as -1.
int32_t mOverrideScalingMode;
bool mCurrentOpacity;
+ bool mBufferLatched = false; // TODO: Use mActiveBuffer?
std::atomic<uint64_t> mCurrentFrameNumber;
+ uint64_t mPreviousFrameNumber; // Only accessed on the main thread.
bool mRefreshPending;
bool mFrameLatencyNeeded;
// Whether filtering is forced on or not
@@ -644,7 +648,7 @@
Condition mQueueItemCondition;
Vector<BufferItem> mQueueItems;
std::atomic<uint64_t> mLastFrameNumberReceived;
- bool mUpdateTexImageFailed; // This is only modified from the main thread
+ bool mUpdateTexImageFailed; // This is only accessed on the main thread.
bool mAutoRefresh;
bool mFreezePositionUpdates;
diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp
index ffaee7a..359ca4e 100644
--- a/services/surfaceflinger/MonitoredProducer.cpp
+++ b/services/surfaceflinger/MonitoredProducer.cpp
@@ -66,8 +66,10 @@
}
status_t MonitoredProducer::dequeueBuffer(int* slot, sp<Fence>* fence,
- uint32_t w, uint32_t h, PixelFormat format, uint32_t usage) {
- return mProducer->dequeueBuffer(slot, fence, w, h, format, usage);
+ uint32_t w, uint32_t h, PixelFormat format, uint32_t usage,
+ FrameEventHistoryDelta* outTimestamps) {
+ return mProducer->dequeueBuffer(
+ slot, fence, w, h, format, usage, outTimestamps);
}
status_t MonitoredProducer::detachBuffer(int slot) {
@@ -145,6 +147,10 @@
outTransformMatrix);
}
+void MonitoredProducer::getFrameTimestamps(FrameEventHistoryDelta* outDelta) {
+ mProducer->getFrameTimestamps(outDelta);
+}
+
status_t MonitoredProducer::getUniqueId(uint64_t* outId) const {
return mProducer->getUniqueId(outId);
}
diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h
index 66f6cf0..17adaa7 100644
--- a/services/surfaceflinger/MonitoredProducer.h
+++ b/services/surfaceflinger/MonitoredProducer.h
@@ -38,7 +38,8 @@
virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers);
virtual status_t setAsyncMode(bool async);
virtual status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w,
- uint32_t h, PixelFormat format, uint32_t usage);
+ uint32_t h, PixelFormat format, uint32_t usage,
+ FrameEventHistoryDelta* outTimestamps);
virtual status_t detachBuffer(int slot);
virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer,
sp<Fence>* outFence);
@@ -63,6 +64,7 @@
virtual IBinder* onAsBinder();
virtual status_t setSharedBufferMode(bool sharedBufferMode) override;
virtual status_t setAutoRefresh(bool autoRefresh) override;
+ virtual void getFrameTimestamps(FrameEventHistoryDelta *outDelta) override;
virtual status_t getUniqueId(uint64_t* outId) const override;
private:
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 533d4ba..73074cb 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -566,6 +566,20 @@
return mGraphicBufferProducerList.indexOf(surfaceTextureBinder) >= 0;
}
+status_t SurfaceFlinger::getSupportedFrameTimestamps(
+ std::vector<FrameEvent>* outSupported) const {
+ *outSupported = {
+ FrameEvent::REQUESTED_PRESENT,
+ FrameEvent::ACQUIRE,
+ FrameEvent::FIRST_REFRESH_START,
+ FrameEvent::GL_COMPOSITION_DONE,
+ getHwComposer().retireFenceRepresentsStartOfScanout() ?
+ FrameEvent::DISPLAY_PRESENT : FrameEvent::DISPLAY_RETIRE,
+ FrameEvent::RELEASE,
+ };
+ return NO_ERROR;
+}
+
status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display,
Vector<DisplayInfo>* configs) {
if ((configs == NULL) || (display.get() == NULL)) {
@@ -1121,14 +1135,14 @@
nsecs_t refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
- preComposition();
+ preComposition(refreshStartTime);
rebuildLayerStacks();
setUpHWComposer();
doDebugFlashRegions();
doComposition();
- postComposition(refreshStartTime);
+ postComposition();
- mPreviousPresentFence = mHwc->getRetireFence(HWC_DISPLAY_PRIMARY);
+ mPreviousPresentFence = mHwc->getPresentFence(HWC_DISPLAY_PRIMARY);
mHadClientComposition = false;
for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) {
@@ -1137,10 +1151,6 @@
mHwc->hasClientComposition(displayDevice->getHwcDisplayId());
}
- // Release any buffers which were replaced this frame
- for (auto& layer : mLayersWithQueuedFrames) {
- layer->releasePendingBuffer();
- }
mLayersWithQueuedFrames.clear();
}
@@ -1188,7 +1198,7 @@
}
}
-void SurfaceFlinger::preComposition()
+void SurfaceFlinger::preComposition(nsecs_t refreshStartTime)
{
ATRACE_CALL();
ALOGV("preComposition");
@@ -1197,7 +1207,7 @@
const LayerVector& layers(mDrawingState.layersSortedByZ);
const size_t count = layers.size();
for (size_t i=0 ; i<count ; i++) {
- if (layers[i]->onPreComposition()) {
+ if (layers[i]->onPreComposition(refreshStartTime)) {
needExtraInvalidate = true;
}
}
@@ -1206,22 +1216,33 @@
}
}
-void SurfaceFlinger::postComposition(nsecs_t refreshStartTime)
+void SurfaceFlinger::postComposition()
{
ATRACE_CALL();
ALOGV("postComposition");
+ // Release any buffers which were replaced this frame
+ for (auto& layer : mLayersWithQueuedFrames) {
+ layer->releasePendingBuffer();
+ }
+
+ bool hadClientComposition = mHwc->hasClientComposition(HWC_DISPLAY_PRIMARY);
+
+ const sp<const DisplayDevice> hw(getDefaultDisplayDevice());
+ sp<Fence> glCompositionDoneFence = hadClientComposition
+ ? hw->getClientTargetAcquireFence()
+ : Fence::NO_FENCE;
const LayerVector& layers(mDrawingState.layersSortedByZ);
const size_t count = layers.size();
for (size_t i=0 ; i<count ; i++) {
- bool frameLatched = layers[i]->onPostComposition();
+ bool frameLatched = layers[i]->onPostComposition(glCompositionDoneFence);
if (frameLatched) {
recordBufferingStats(layers[i]->getName().string(),
layers[i]->getOccupancyHistory(false));
}
}
- sp<Fence> presentFence = mHwc->getRetireFence(HWC_DISPLAY_PRIMARY);
+ sp<Fence> presentFence = mHwc->getPresentFence(HWC_DISPLAY_PRIMARY);
if (presentFence->isValid()) {
if (mPrimaryDispSync.addPresentFence(presentFence)) {
@@ -1231,16 +1252,12 @@
}
}
- const sp<const DisplayDevice> hw(getDefaultDisplayDevice());
if (kIgnorePresentFences) {
if (hw->isDisplayOn()) {
enableHardwareVsync();
}
}
- mFenceTracker.addFrame(refreshStartTime, presentFence,
- hw->getVisibleLayersSortedByZ(), hw->getClientTargetAcquireFence());
-
if (mAnimCompositionPending) {
mAnimCompositionPending = false;
@@ -1463,7 +1480,7 @@
}
const auto hwcId = displayDevice->getHwcDisplayId();
if (hwcId >= 0) {
- mHwc->commit(hwcId);
+ mHwc->presentAndGetReleaseFences(hwcId);
}
displayDevice->onSwapBuffersCompleted();
displayDevice->makeCurrent(mEGLDisplay, mEGLContext);
@@ -1997,6 +2014,7 @@
{
ALOGV("handlePageFlip");
+ nsecs_t latchTime = systemTime();
Region dirtyRegion;
bool visibleRegions = false;
@@ -2026,7 +2044,7 @@
}
}
for (auto& layer : mLayersWithQueuedFrames) {
- const Region dirty(layer->latchBuffer(visibleRegions));
+ const Region dirty(layer->latchBuffer(visibleRegions, latchTime));
layer->useSurfaceDamage();
const Layer::State& s(layer->getDrawingState());
invalidateLayerStack(s.layerStack, dirty);
@@ -2824,9 +2842,9 @@
}
if ((index < numArgs) &&
- (args[index] == String16("--fences"))) {
+ (args[index] == String16("--frame-events"))) {
index++;
- mFenceTracker.dump(&result);
+ dumpFrameEventsLocked(result);
dumpAll = false;
}
}
@@ -2967,6 +2985,16 @@
}
}
+void SurfaceFlinger::dumpFrameEventsLocked(String8& result) {
+ result.appendFormat("Layer frame timestamps:\n");
+
+ const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
+ const size_t count = currentLayers.size();
+ for (size_t i=0 ; i<count ; i++) {
+ currentLayers[i]->dumpFrameEvents(result);
+ }
+}
+
void SurfaceFlinger::dumpBufferingStats(String8& result) const {
result.append("Buffering stats:\n");
result.append(" [Layer name] <Active time> <Two buffer> "
@@ -3886,11 +3914,6 @@
}
}
-bool SurfaceFlinger::getFrameTimestamps(const Layer& layer,
- uint64_t frameNumber, FrameTimestamps* outTimestamps) {
- return mFenceTracker.getFrameTimestamps(layer, frameNumber, outTimestamps);
-}
-
// ---------------------------------------------------------------------------
SurfaceFlinger::LayerVector::LayerVector() {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 49964ec..451d661 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -53,7 +53,6 @@
#include "Barrier.h"
#include "DisplayDevice.h"
#include "DispSync.h"
-#include "FenceTracker.h"
#include "FrameTracker.h"
#include "MessageQueue.h"
#include "SurfaceInterceptor.h"
@@ -200,6 +199,8 @@
virtual void bootFinished();
virtual bool authenticateSurfaceTexture(
const sp<IGraphicBufferProducer>& bufferProducer) const;
+ virtual status_t getSupportedFrameTimestamps(
+ std::vector<FrameEvent>* outSupported) const;
virtual sp<IDisplayEventConnection> createDisplayEventConnection();
virtual status_t captureScreen(const sp<IBinder>& display,
const sp<IGraphicBufferProducer>& producer,
@@ -399,8 +400,8 @@
const LayerVector& currentLayers, uint32_t layerStack,
Region& dirtyRegion, Region& opaqueRegion);
- void preComposition();
- void postComposition(nsecs_t refreshStartTime);
+ void preComposition(nsecs_t refreshStartTime);
+ void postComposition();
void rebuildLayerStacks();
void setUpHWComposer();
void doComposition();
@@ -444,14 +445,13 @@
void logFrameStats();
void dumpStaticScreenStats(String8& result) const;
+ // Not const because each Layer needs to query Fences and cache timestamps.
+ void dumpFrameEventsLocked(String8& result);
void recordBufferingStats(const char* layerName,
std::vector<OccupancyTracker::Segment>&& history);
void dumpBufferingStats(String8& result) const;
- bool getFrameTimestamps(const Layer& layer, uint64_t frameNumber,
- FrameTimestamps* outTimestamps);
-
/* ------------------------------------------------------------------------
* Attributes
*/
@@ -517,7 +517,6 @@
nsecs_t mLastTransactionTime;
bool mBootFinished;
bool mForceFullDamage;
- FenceTracker mFenceTracker;
#ifdef USE_HWC2
bool mPropagateBackpressure = true;
#endif
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.cpp b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
index 6f2520b..029937a 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.cpp
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
@@ -189,10 +189,14 @@
return nextRefresh + extraPadding;
}
+sp<Fence> SurfaceFlingerConsumer::getPrevFinalReleaseFence() const {
+ Mutex::Autolock lock(mMutex);
+ return ConsumerBase::mPrevFinalReleaseFence;
+}
+
#ifdef USE_HWC2
void SurfaceFlingerConsumer::setReleaseFence(const sp<Fence>& fence)
{
- mPrevReleaseFence = fence;
if (!mPendingRelease.isPending) {
GLConsumer::setReleaseFence(fence);
return;
@@ -222,17 +226,8 @@
strerror(-result), result);
mPendingRelease = PendingRelease();
}
-#else
-void SurfaceFlingerConsumer::setReleaseFence(const sp<Fence>& fence) {
- mPrevReleaseFence = fence;
- GLConsumer::setReleaseFence(fence);
-}
#endif
-sp<Fence> SurfaceFlingerConsumer::getPrevReleaseFence() const {
- return mPrevReleaseFence;
-}
-
void SurfaceFlingerConsumer::setContentsChangedListener(
const wp<ContentsChangedListener>& listener) {
setFrameAvailableListener(listener);
@@ -253,10 +248,13 @@
}
}
-bool SurfaceFlingerConsumer::getFrameTimestamps(uint64_t frameNumber,
- FrameTimestamps* outTimestamps) const {
- sp<const Layer> l = mLayer.promote();
- return l.get() ? l->getFrameTimestamps(frameNumber, outTimestamps) : false;
+void SurfaceFlingerConsumer::addAndGetFrameTimestamps(
+ const NewFrameEventsEntry* newTimestamps,
+ FrameEventHistoryDelta *outDelta) {
+ sp<Layer> l = mLayer.promote();
+ if (l.get()) {
+ l->addAndGetFrameTimestamps(newTimestamps, outDelta);
+ }
}
// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.h b/services/surfaceflinger/SurfaceFlingerConsumer.h
index 4271039..d3f0070 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.h
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.h
@@ -37,10 +37,9 @@
};
SurfaceFlingerConsumer(const sp<IGraphicBufferConsumer>& consumer,
- uint32_t tex, const Layer* layer)
+ uint32_t tex, Layer* layer)
: GLConsumer(consumer, tex, GLConsumer::TEXTURE_EXTERNAL, false, false),
- mTransformToDisplayInverse(false), mSurfaceDamage(),
- mPrevReleaseFence(Fence::NO_FENCE), mLayer(layer)
+ mTransformToDisplayInverse(false), mSurfaceDamage(), mLayer(layer)
{}
class BufferRejecter {
@@ -61,7 +60,7 @@
// texture.
status_t updateTexImage(BufferRejecter* rejecter, const DispSync& dispSync,
bool* autoRefresh, bool* queuedBuffer,
- uint64_t maxFrameNumber = 0);
+ uint64_t maxFrameNumber);
// See GLConsumer::bindTextureImageLocked().
status_t bindTextureImage();
@@ -79,14 +78,15 @@
nsecs_t computeExpectedPresent(const DispSync& dispSync);
- virtual void setReleaseFence(const sp<Fence>& fence) override;
- sp<Fence> getPrevReleaseFence() const;
+ sp<Fence> getPrevFinalReleaseFence() const;
#ifdef USE_HWC2
+ virtual void setReleaseFence(const sp<Fence>& fence) override;
void releasePendingBuffer();
#endif
- virtual bool getFrameTimestamps(uint64_t frameNumber,
- FrameTimestamps* outTimestamps) const override;
+ virtual void addAndGetFrameTimestamps(
+ const NewFrameEventsEntry* newTimestamps,
+ FrameEventHistoryDelta* outDelta) override;
private:
virtual void onSidebandStreamChanged();
@@ -107,11 +107,8 @@
PendingRelease mPendingRelease;
#endif
- // The release fence of the already displayed buffer (previous frame).
- sp<Fence> mPrevReleaseFence;
-
// The layer for this SurfaceFlingerConsumer
- wp<const Layer> mLayer;
+ const wp<Layer> mLayer;
};
// ----------------------------------------------------------------------------
diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
index 8e5c565..66a3c42 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -599,6 +599,19 @@
return mGraphicBufferProducerList.indexOf(surfaceTextureBinder) >= 0;
}
+status_t SurfaceFlinger::getSupportedFrameTimestamps(
+ std::vector<FrameEvent>* outSupported) const {
+ *outSupported = {
+ FrameEvent::REQUESTED_PRESENT,
+ FrameEvent::ACQUIRE,
+ FrameEvent::FIRST_REFRESH_START,
+ FrameEvent::GL_COMPOSITION_DONE,
+ FrameEvent::DISPLAY_RETIRE,
+ FrameEvent::RELEASE,
+ };
+ return NO_ERROR;
+}
+
status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display,
Vector<DisplayInfo>* configs) {
if ((configs == NULL) || (display.get() == NULL)) {
@@ -1055,12 +1068,12 @@
nsecs_t refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
- preComposition();
+ preComposition(refreshStartTime);
rebuildLayerStacks();
setUpHWComposer();
doDebugFlashRegions();
doComposition();
- postComposition(refreshStartTime);
+ postComposition();
}
void SurfaceFlinger::doDebugFlashRegions()
@@ -1103,13 +1116,13 @@
}
}
-void SurfaceFlinger::preComposition()
+void SurfaceFlinger::preComposition(nsecs_t refreshStartTime)
{
bool needExtraInvalidate = false;
const LayerVector& layers(mDrawingState.layersSortedByZ);
const size_t count = layers.size();
for (size_t i=0 ; i<count ; i++) {
- if (layers[i]->onPreComposition()) {
+ if (layers[i]->onPreComposition(refreshStartTime)) {
needExtraInvalidate = true;
}
}
@@ -1118,19 +1131,28 @@
}
}
-void SurfaceFlinger::postComposition(nsecs_t refreshStartTime)
+void SurfaceFlinger::postComposition()
{
+ const HWComposer& hwc = getHwComposer();
+ const sp<const DisplayDevice> hw(getDefaultDisplayDevice());
+
+ bool hadGlesComposition =
+ getHwComposer().hasGlesComposition(hw->getHwcDisplayId());
+ sp<Fence> glCompositionDoneFence = hadGlesComposition
+ ? hw->getClientTargetAcquireFence()
+ : Fence::NO_FENCE;
+
const LayerVector& layers(mDrawingState.layersSortedByZ);
const size_t count = layers.size();
for (size_t i=0 ; i<count ; i++) {
- bool frameLatched = layers[i]->onPostComposition();
+ bool frameLatched = layers[i]->onPostComposition(
+ glCompositionDoneFence);
if (frameLatched) {
recordBufferingStats(layers[i]->getName().string(),
layers[i]->getOccupancyHistory(false));
}
}
- const HWComposer& hwc = getHwComposer();
sp<Fence> presentFence = hwc.getDisplayFence(HWC_DISPLAY_PRIMARY);
if (presentFence->isValid()) {
@@ -1141,16 +1163,12 @@
}
}
- const sp<const DisplayDevice> hw(getDefaultDisplayDevice());
if (kIgnorePresentFences) {
if (hw->isDisplayOn()) {
enableHardwareVsync();
}
}
- mFenceTracker.addFrame(refreshStartTime, presentFence,
- hw->getVisibleLayersSortedByZ(), hw->getClientTargetAcquireFence());
-
if (mAnimCompositionPending) {
mAnimCompositionPending = false;
@@ -1918,6 +1936,7 @@
bool SurfaceFlinger::handlePageFlip()
{
+ nsecs_t latchTime = systemTime();
Region dirtyRegion;
bool visibleRegions = false;
@@ -1949,7 +1968,7 @@
}
for (size_t i = 0, count = layersWithQueuedFrames.size() ; i<count ; i++) {
Layer* layer = layersWithQueuedFrames[i];
- const Region dirty(layer->latchBuffer(visibleRegions));
+ const Region dirty(layer->latchBuffer(visibleRegions, latchTime));
layer->useSurfaceDamage();
const Layer::State& s(layer->getDrawingState());
invalidateLayerStack(s.layerStack, dirty);
@@ -2741,9 +2760,9 @@
}
if ((index < numArgs) &&
- (args[index] == String16("--fences"))) {
+ (args[index] == String16("--frame-events"))) {
index++;
- mFenceTracker.dump(&result);
+ dumpFrameEventsLocked(result);
dumpAll = false;
}
}
@@ -2866,6 +2885,16 @@
NUM_BUCKETS - 1, bucketTimeSec, percent);
}
+void SurfaceFlinger::dumpFrameEventsLocked(String8& result) {
+ result.appendFormat("Layer frame timestamps:\n");
+
+ const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
+ const size_t count = currentLayers.size();
+ for (size_t i=0 ; i<count ; i++) {
+ currentLayers[i]->dumpFrameEvents(result);
+ }
+}
+
void SurfaceFlinger::recordBufferingStats(const char* layerName,
std::vector<OccupancyTracker::Segment>&& history) {
Mutex::Autolock lock(mBufferingStatsMutex);
@@ -3740,11 +3769,6 @@
return result;
}
-bool SurfaceFlinger::getFrameTimestamps(const Layer& layer,
- uint64_t frameNumber, FrameTimestamps* outTimestamps) {
- return mFenceTracker.getFrameTimestamps(layer, frameNumber, outTimestamps);
-}
-
void SurfaceFlinger::checkScreenshot(size_t w, size_t s, size_t h, void const* vaddr,
const sp<const DisplayDevice>& hw, uint32_t minLayerZ, uint32_t maxLayerZ) {
if (DEBUG_SCREENSHOTS) {