Merge "fix broken logTimeStat stuff"
diff --git a/include/gui/ISurfaceTexture.h b/include/gui/ISurfaceTexture.h
index 168310c..6ed3c6f 100644
--- a/include/gui/ISurfaceTexture.h
+++ b/include/gui/ISurfaceTexture.h
@@ -62,8 +62,11 @@
// contents of the buffer associated with slot and transfers ownership of
// that slot back to the server. It is not valid to call queueBuffer on a
// slot that is not owned by the client or one for which a buffer associated
- // via requestBuffer.
- virtual status_t queueBuffer(int slot) = 0;
+ // via requestBuffer. In addition, a timestamp must be provided by the
+ // client for this buffer. The timestamp is measured in nanoseconds, and
+ // must be monotonically increasing. Its other properties (zero point, etc)
+ // are client-dependent, and should be documented by the client.
+ virtual status_t queueBuffer(int slot, int64_t timestamp) = 0;
// cancelBuffer indicates that the client does not wish to fill in the
// buffer associated with slot and transfers ownership of the slot back to
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h
index 9bf38f7..afa64d3 100644
--- a/include/gui/SurfaceTexture.h
+++ b/include/gui/SurfaceTexture.h
@@ -66,7 +66,12 @@
// unmodified.
virtual status_t dequeueBuffer(int *buf);
- virtual status_t queueBuffer(int buf);
+ // queueBuffer returns a filled buffer to the SurfaceTexture. In addition, a
+ // timestamp must be provided for the buffer. The timestamp is in
+ // nanoseconds, and must be monotonically increasing. Its other semantics
+ // (zero point, etc) are client-dependent and should be documented by the
+ // client.
+ virtual status_t queueBuffer(int buf, int64_t timestamp);
virtual void cancelBuffer(int buf);
virtual status_t setCrop(const Rect& reg);
virtual status_t setTransform(uint32_t transform);
@@ -98,6 +103,14 @@
// functions.
void getTransformMatrix(float mtx[16]);
+ // getTimestamp retrieves the timestamp associated with the texture image
+ // set by the most recent call to updateTexImage.
+ //
+ // The timestamp is in nanoseconds, and is monotonically increasing. Its
+ // other semantics (zero point, etc) are source-dependent and should be
+ // documented by the source.
+ int64_t getTimestamp();
+
// setFrameAvailableListener sets the listener object that will be notified
// when a new frame becomes available.
void setFrameAvailableListener(const sp<FrameAvailableListener>& l);
@@ -172,6 +185,10 @@
// gets set to mLastQueuedTransform each time updateTexImage is called.
uint32_t mCurrentTransform;
+ // mCurrentTimestamp is the timestamp for the current texture. It
+ // gets set to mLastQueuedTimestamp each time updateTexImage is called.
+ int64_t mCurrentTimestamp;
+
// mLastQueued is the buffer slot index of the most recently enqueued buffer.
// At construction time it is initialized to INVALID_BUFFER_SLOT, and is
// updated each time queueBuffer is called.
@@ -187,6 +204,10 @@
// queueBuffer gets called.
uint32_t mLastQueuedTransform;
+ // mLastQueuedTimestamp is the timestamp for the buffer that was most
+ // recently queued. This gets set by queueBuffer.
+ int64_t mLastQueuedTimestamp;
+
// mNextCrop is the crop rectangle that will be used for the next buffer
// that gets queued. It is set by calling setCrop.
Rect mNextCrop;
diff --git a/include/gui/SurfaceTextureClient.h b/include/gui/SurfaceTextureClient.h
index 4cdece9..df82bf2 100644
--- a/include/gui/SurfaceTextureClient.h
+++ b/include/gui/SurfaceTextureClient.h
@@ -33,6 +33,8 @@
public:
SurfaceTextureClient(const sp<ISurfaceTexture>& surfaceTexture);
+ sp<ISurfaceTexture> getISurfaceTexture() const;
+
private:
// can't be copied
@@ -61,6 +63,7 @@
int dispatchSetBufferCount(va_list args);
int dispatchSetBuffersGeometry(va_list args);
int dispatchSetBuffersTransform(va_list args);
+ int dispatchSetBuffersTimestamp(va_list args);
int dispatchSetCrop(va_list args);
int dispatchSetUsage(va_list args);
@@ -69,6 +72,7 @@
int setBufferCount(int bufferCount);
int setBuffersGeometry(int w, int h, int format);
int setBuffersTransform(int transform);
+ int setBuffersTimestamp(int64_t timestamp);
int setCrop(Rect const* rect);
int setUsage(uint32_t reqUsage);
@@ -112,6 +116,11 @@
// at the next deuque operation. It is initialized to 0.
uint32_t mReqUsage;
+ // mTimestamp is the timestamp that will be used for the next buffer queue
+ // operation. It defaults to NATIVE_WINDOW_TIMESTAMP_AUTO, which means that
+ // a timestamp is auto-generated when queueBuffer is called.
+ int64_t mTimestamp;
+
// mMutex is the mutex used to prevent concurrent access to the member
// variables of SurfaceTexture objects. It must be locked whenever the
// member variables are accessed.
diff --git a/include/surfaceflinger/Surface.h b/include/surfaceflinger/Surface.h
index 9e0b5bb..a59d9e5 100644
--- a/include/surfaceflinger/Surface.h
+++ b/include/surfaceflinger/Surface.h
@@ -226,7 +226,8 @@
int dispatch_set_buffer_count(va_list args);
int dispatch_set_buffers_geometry(va_list args);
int dispatch_set_buffers_transform(va_list args);
-
+ int dispatch_set_buffers_timestamp(va_list args);
+
void setUsage(uint32_t reqUsage);
int connect(int api);
int disconnect(int api);
@@ -234,6 +235,7 @@
int setBufferCount(int bufferCount);
int setBuffersGeometry(int w, int h, int format);
int setBuffersTransform(int transform);
+ int setBuffersTimestamp(int64_t timestamp);
/*
* private stuff...
diff --git a/include/ui/Input.h b/include/ui/Input.h
index e9cacf4..8e8b61b 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -311,6 +311,13 @@
inline int32_t getAction() const { return mAction; }
+ inline int32_t getActionMasked() const { return mAction & AMOTION_EVENT_ACTION_MASK; }
+
+ inline int32_t getActionIndex() const {
+ return (mAction & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
+ >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+ }
+
inline void setAction(int32_t action) { mAction = action; }
inline int32_t getFlags() const { return mFlags; }
@@ -458,6 +465,8 @@
AMOTION_EVENT_AXIS_ORIENTATION, pointerIndex, historicalIndex);
}
+ ssize_t findPointerIndex(int32_t pointerId) const;
+
void initialize(
int32_t deviceId,
int32_t source,
@@ -551,8 +560,7 @@
};
/*
- * Calculates the velocity of pointer motions over time.
- * Uses essentially the same algorithm as android.view.VelocityTracker.
+ * Calculates the velocity of pointer movements over time.
*/
class VelocityTracker {
public:
@@ -565,6 +573,11 @@
// Resets the velocity tracker state.
void clear();
+ // Resets the velocity tracker state for specific pointers.
+ // Call this method when some pointers have changed and may be reusing
+ // an id that was assigned to a different pointer earlier.
+ void clearPointers(BitSet32 idBits);
+
// Adds movement information for a set of pointers.
// The idBits bitfield specifies the pointer ids of the pointers whose positions
// are included in the movement.
@@ -572,11 +585,20 @@
// increasing id. Its size should be equal to the number of one bits in idBits.
void addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions);
+ // Adds movement information for all pointers in a MotionEvent, including historical samples.
+ void addMovement(const MotionEvent* event);
+
// Gets the velocity of the specified pointer id in position units per second.
// Returns false and sets the velocity components to zero if there is no movement
// information for the pointer.
bool getVelocity(uint32_t id, float* outVx, float* outVy) const;
+ // Gets the active pointer id, or -1 if none.
+ inline int32_t getActivePointerId() const { return mActivePointerId; }
+
+ // Gets a bitset containing all pointer ids from the most recent movement.
+ inline BitSet32 getCurrentPointerIdBits() const { return mMovements[mIndex].idBits; }
+
private:
// Number of samples to keep.
static const uint32_t HISTORY_SIZE = 10;
@@ -585,7 +607,7 @@
static const nsecs_t MAX_AGE = 200 * 1000000; // 200 ms
// The minimum duration between samples when estimating velocity.
- static const nsecs_t MIN_DURATION = 5 * 1000000; // 5 ms
+ static const nsecs_t MIN_DURATION = 10 * 1000000; // 10 ms
struct Movement {
nsecs_t eventTime;
@@ -595,6 +617,7 @@
uint32_t mIndex;
Movement mMovements[HISTORY_SIZE];
+ int32_t mActivePointerId;
};
/*
diff --git a/include/ui/KeycodeLabels.h b/include/ui/KeycodeLabels.h
index b912e9b..8383957 100755
--- a/include/ui/KeycodeLabels.h
+++ b/include/ui/KeycodeLabels.h
@@ -228,6 +228,9 @@
{ "BUTTON_14", 201 },
{ "BUTTON_15", 202 },
{ "BUTTON_16", 203 },
+ { "LANGUAGE_SWITCH", 204 },
+ { "MANNER_MODE", 205 },
+ { "3D_MODE", 206 },
// NOTE: If you add a new keycode here you must also add it to several other files.
// Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
diff --git a/include/ui/egl/android_natives.h b/include/ui/egl/android_natives.h
index 0ac34d0..0a6e4fb 100644
--- a/include/ui/egl/android_natives.h
+++ b/include/ui/egl/android_natives.h
@@ -57,7 +57,7 @@
{
/* a magic value defined by the actual EGL native type */
int magic;
-
+
/* the sizeof() of the actual EGL native type */
int version;
@@ -110,6 +110,14 @@
* conjunction with this query.
*/
NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER,
+
+ /* Get the concrete type of a ANativeWindow. See below for the list of
+ * possible return values.
+ *
+ * This query should not be used outside the Android framework and will
+ * likely be removed in the near future.
+ */
+ NATIVE_WINDOW_CONCRETE_TYPE,
};
/* valid operations for the (*perform)() hook */
@@ -121,6 +129,7 @@
NATIVE_WINDOW_SET_BUFFER_COUNT,
NATIVE_WINDOW_SET_BUFFERS_GEOMETRY,
NATIVE_WINDOW_SET_BUFFERS_TRANSFORM,
+ NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP,
};
/* parameter for NATIVE_WINDOW_[DIS]CONNECT */
@@ -142,7 +151,22 @@
NATIVE_WINDOW_TRANSFORM_ROT_270 = HAL_TRANSFORM_ROT_270,
};
-struct ANativeWindow
+/* values returned by the NATIVE_WINDOW_CONCRETE_TYPE query */
+enum {
+ NATIVE_WINDOW_FRAMEBUFFER, // FramebufferNativeWindow
+ NATIVE_WINDOW_SURFACE, // Surface
+ NATIVE_WINDOW_SURFACE_TEXTURE_CLIENT, // SurfaceTextureClient
+};
+
+/* parameter for NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP
+ *
+ * Special timestamp value to indicate that timestamps should be auto-generated
+ * by the native window when queueBuffer is called. This is equal to INT64_MIN,
+ * defined directly to avoid problems with C99/C++ inclusion of stdint.h.
+ */
+const int64_t NATIVE_WINDOW_TIMESTAMP_AUTO = (-9223372036854775807LL-1);
+
+struct ANativeWindow
{
#ifdef __cplusplus
ANativeWindow()
@@ -247,7 +271,8 @@
* NATIVE_WINDOW_SET_BUFFER_COUNT
* NATIVE_WINDOW_SET_BUFFERS_GEOMETRY
* NATIVE_WINDOW_SET_BUFFERS_TRANSFORM
- *
+ * NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP
+ *
*/
int (*perform)(struct ANativeWindow* window,
@@ -374,6 +399,22 @@
transform);
}
+/*
+ * native_window_set_buffers_timestamp(..., int64_t timestamp)
+ * All buffers queued after this call will be associated with the timestamp
+ * parameter specified. If the timestamp is set to NATIVE_WINDOW_TIMESTAMP_AUTO
+ * (the default), timestamps will be generated automatically when queueBuffer is
+ * called. The timestamp is measured in nanoseconds, and must be monotonically
+ * increasing.
+ */
+static inline int native_window_set_buffers_timestamp(
+ ANativeWindow* window,
+ int64_t timestamp)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP,
+ timestamp);
+}
+
// ---------------------------------------------------------------------------
/* FIXME: this is legacy for pixmaps */
diff --git a/include/utils/BitSet.h b/include/utils/BitSet.h
index f03825a..de748b5 100644
--- a/include/utils/BitSet.h
+++ b/include/utils/BitSet.h
@@ -61,6 +61,10 @@
// Result is undefined if all bits are marked.
inline uint32_t firstUnmarkedBit() const { return __builtin_clz(~ value); }
+ // Finds the last marked bit in the set.
+ // Result is undefined if all bits are unmarked.
+ inline uint32_t lastMarkedBit() const { return 31 - __builtin_ctz(value); }
+
// Gets the index of the specified bit in the set, which is the number of
// marked bits that appear before the specified bit.
inline uint32_t getIndexOfBit(uint32_t n) const {
diff --git a/include/utils/Functor.h b/include/utils/Functor.h
index 565f4a3..e24ded4 100644
--- a/include/utils/Functor.h
+++ b/include/utils/Functor.h
@@ -25,8 +25,7 @@
public:
Functor() {}
virtual ~Functor() {}
- virtual status_t operator ()() { return true; }
- virtual status_t operator ()(float* data, uint32_t len) { return true; }
+ virtual status_t operator ()(int what, void* data) { return NO_ERROR; }
};
}; // namespace android
diff --git a/include/utils/String8.h b/include/utils/String8.h
index 6b49ff5..4163697 100644
--- a/include/utils/String8.h
+++ b/include/utils/String8.h
@@ -165,8 +165,8 @@
String8 walkPath(String8* outRemains = NULL) const;
/*
- * Return the filename extension. This is the last '.' and up to
- * four characters that follow it. The '.' is included in case we
+ * Return the filename extension. This is the last '.' and any number
+ * of characters that follow it. The '.' is included in case we
* decide to expand our definition of what constitutes an extension.
*
* "/tmp/foo/bar.c" --> ".c"
diff --git a/include/utils/Timers.h b/include/utils/Timers.h
index 9a9e07c..8b4d322 100644
--- a/include/utils/Timers.h
+++ b/include/utils/Timers.h
@@ -88,6 +88,16 @@
nsecs_t systemTime(int clock);
#endif // def __cplusplus
+/**
+ * Returns the number of milliseconds to wait between the reference time and the timeout time.
+ * If the timeout is in the past relative to the reference time, returns 0.
+ * If the timeout is more than INT_MAX milliseconds in the future relative to the reference time,
+ * such as when timeoutTime == LLONG_MAX, returns -1 to indicate an infinite timeout delay.
+ * Otherwise, returns the difference between the reference time and timeout time
+ * rounded up to the next millisecond.
+ */
+int toMillisecondTimeoutDelay(nsecs_t referenceTime, nsecs_t timeoutTime);
+
#ifdef __cplusplus
} // extern "C"
#endif
diff --git a/include/utils/Vector.h b/include/utils/Vector.h
index ec851bd..6fd307f 100644
--- a/include/utils/Vector.h
+++ b/include/utils/Vector.h
@@ -162,6 +162,9 @@
inline status_t sort(compar_t cmp);
inline status_t sort(compar_r_t cmp, void* state);
+ // for debugging only
+ inline size_t getItemSize() const { return itemSize(); }
+
protected:
virtual void do_construct(void* storage, size_t num) const;
virtual void do_destroy(void* storage, size_t num) const;
diff --git a/libs/gui/ISurfaceTexture.cpp b/libs/gui/ISurfaceTexture.cpp
index d661fd5..bc14ad5 100644
--- a/libs/gui/ISurfaceTexture.cpp
+++ b/libs/gui/ISurfaceTexture.cpp
@@ -88,10 +88,11 @@
return result;
}
- virtual status_t queueBuffer(int buf) {
+ virtual status_t queueBuffer(int buf, int64_t timestamp) {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
data.writeInt32(buf);
+ data.writeInt64(timestamp);
remote()->transact(QUEUE_BUFFER, data, &reply);
status_t result = reply.readInt32();
return result;
@@ -174,7 +175,8 @@
case QUEUE_BUFFER: {
CHECK_INTERFACE(ISurfaceTexture, data, reply);
int buf = data.readInt32();
- status_t result = queueBuffer(buf);
+ int64_t timestamp = data.readInt64();
+ status_t result = queueBuffer(buf, timestamp);
reply->writeInt32(result);
return NO_ERROR;
} break;
@@ -196,7 +198,6 @@
return NO_ERROR;
} break;
case SET_TRANSFORM: {
- Rect reg;
CHECK_INTERFACE(ISurfaceTexture, data, reply);
uint32_t transform = data.readInt32();
status_t result = setTransform(transform);
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index 3bed959..cdaca47 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -76,9 +76,15 @@
static void mtxMul(float out[16], const float a[16], const float b[16]);
SurfaceTexture::SurfaceTexture(GLuint tex) :
- mBufferCount(MIN_BUFFER_SLOTS), mCurrentTexture(INVALID_BUFFER_SLOT),
- mCurrentTransform(0), mLastQueued(INVALID_BUFFER_SLOT),
- mLastQueuedTransform(0), mNextTransform(0), mTexName(tex) {
+ mBufferCount(MIN_BUFFER_SLOTS),
+ mCurrentTexture(INVALID_BUFFER_SLOT),
+ mCurrentTransform(0),
+ mCurrentTimestamp(0),
+ mLastQueued(INVALID_BUFFER_SLOT),
+ mLastQueuedTransform(0),
+ mLastQueuedTimestamp(0),
+ mNextTransform(0),
+ mTexName(tex) {
LOGV("SurfaceTexture::SurfaceTexture");
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
mSlots[i].mEglImage = EGL_NO_IMAGE_KHR;
@@ -153,7 +159,7 @@
return OK;
}
-status_t SurfaceTexture::queueBuffer(int buf) {
+status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp) {
LOGV("SurfaceTexture::queueBuffer");
Mutex::Autolock lock(mMutex);
if (buf < 0 || mBufferCount <= buf) {
@@ -172,6 +178,7 @@
mLastQueued = buf;
mLastQueuedCrop = mNextCrop;
mLastQueuedTransform = mNextTransform;
+ mLastQueuedTimestamp = timestamp;
if (mFrameAvailableListener != 0) {
mFrameAvailableListener->onFrameAvailable();
}
@@ -246,12 +253,13 @@
mCurrentTextureBuf = mSlots[mCurrentTexture].mGraphicBuffer;
mCurrentCrop = mLastQueuedCrop;
mCurrentTransform = mLastQueuedTransform;
+ mCurrentTimestamp = mLastQueuedTimestamp;
}
return OK;
}
void SurfaceTexture::getTransformMatrix(float mtx[16]) {
- LOGV("SurfaceTexture::updateTexImage");
+ LOGV("SurfaceTexture::getTransformMatrix");
Mutex::Autolock lock(mMutex);
float xform[16];
@@ -283,11 +291,42 @@
sp<GraphicBuffer>& buf(mSlots[mCurrentTexture].mGraphicBuffer);
float tx, ty, sx, sy;
if (!mCurrentCrop.isEmpty()) {
- tx = float(mCurrentCrop.left) / float(buf->getWidth());
- ty = float(buf->getHeight() - mCurrentCrop.bottom) /
- float(buf->getHeight());
- sx = float(mCurrentCrop.width()) / float(buf->getWidth());
- sy = float(mCurrentCrop.height()) / float(buf->getHeight());
+ // In order to prevent bilinear sampling at the of the crop rectangle we
+ // may need to shrink it by 2 texels in each direction. Normally this
+ // would just need to take 1/2 a texel off each end, but because the
+ // chroma channels will likely be subsampled we need to chop off a whole
+ // texel. This will cause artifacts if someone does nearest sampling
+ // with 1:1 pixel:texel ratio, but it's impossible to simultaneously
+ // accomodate the bilinear and nearest sampling uses.
+ //
+ // If nearest sampling turns out to be a desirable usage of these
+ // textures then we could add the ability to switch a SurfaceTexture to
+ // nearest-mode. Preferably, however, the image producers (video
+ // decoder, camera, etc.) would simply not use a crop rectangle (or at
+ // least not tell the framework about it) so that the GPU can do the
+ // correct edge behavior.
+ int xshrink = 0, yshrink = 0;
+ if (mCurrentCrop.left > 0) {
+ tx = float(mCurrentCrop.left + 1) / float(buf->getWidth());
+ xshrink++;
+ } else {
+ tx = 0.0f;
+ }
+ if (mCurrentCrop.right < buf->getWidth()) {
+ xshrink++;
+ }
+ if (mCurrentCrop.bottom < buf->getHeight()) {
+ ty = (float(buf->getHeight() - mCurrentCrop.bottom) + 1.0f) /
+ float(buf->getHeight());
+ yshrink++;
+ } else {
+ ty = 0.0f;
+ }
+ if (mCurrentCrop.top > 0) {
+ yshrink++;
+ }
+ sx = float(mCurrentCrop.width() - xshrink) / float(buf->getWidth());
+ sy = float(mCurrentCrop.height() - yshrink) / float(buf->getHeight());
} else {
tx = 0.0f;
ty = 0.0f;
@@ -298,7 +337,7 @@
sx, 0, 0, 0,
0, sy, 0, 0,
0, 0, 1, 0,
- sx*tx, sy*ty, 0, 1,
+ tx, ty, 0, 1,
};
float mtxBeforeFlipV[16];
@@ -311,6 +350,12 @@
mtxMul(mtx, mtxFlipV, mtxBeforeFlipV);
}
+nsecs_t SurfaceTexture::getTimestamp() {
+ LOGV("SurfaceTexture::getTimestamp");
+ Mutex::Autolock lock(mMutex);
+ return mCurrentTimestamp;
+}
+
void SurfaceTexture::setFrameAvailableListener(
const sp<FrameAvailableListener>& l) {
LOGV("SurfaceTexture::setFrameAvailableListener");
diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp
index a40fac9..a4812d0 100644
--- a/libs/gui/SurfaceTextureClient.cpp
+++ b/libs/gui/SurfaceTextureClient.cpp
@@ -26,7 +26,8 @@
SurfaceTextureClient::SurfaceTextureClient(
const sp<ISurfaceTexture>& surfaceTexture):
mSurfaceTexture(surfaceTexture), mAllocator(0), mReqWidth(1),
- mReqHeight(1), mReqFormat(DEFAULT_FORMAT), mReqUsage(0), mMutex() {
+ mReqHeight(1), mReqFormat(DEFAULT_FORMAT), mReqUsage(0),
+ mTimestamp(NATIVE_WINDOW_TIMESTAMP_AUTO), mMutex() {
// Initialize the ANativeWindow function pointers.
ANativeWindow::setSwapInterval = setSwapInterval;
ANativeWindow::dequeueBuffer = dequeueBuffer;
@@ -40,6 +41,10 @@
mAllocator = mSurfaceTexture->getAllocator();
}
+sp<ISurfaceTexture> SurfaceTextureClient::getISurfaceTexture() const {
+ return mSurfaceTexture;
+}
+
int SurfaceTextureClient::setSwapInterval(ANativeWindow* window, int interval) {
SurfaceTextureClient* c = getSelf(window);
return c->setSwapInterval(interval);
@@ -131,9 +136,17 @@
int SurfaceTextureClient::queueBuffer(android_native_buffer_t* buffer) {
LOGV("SurfaceTextureClient::queueBuffer");
Mutex::Autolock lock(mMutex);
+ int64_t timestamp;
+ if (mTimestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) {
+ timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
+ LOGV("SurfaceTextureClient::queueBuffer making up timestamp: %.2f ms",
+ timestamp / 1000000.f);
+ } else {
+ timestamp = mTimestamp;
+ }
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
if (mSlots[i]->handle == buffer->handle) {
- return mSurfaceTexture->queueBuffer(i);
+ return mSurfaceTexture->queueBuffer(i, timestamp);
}
}
LOGE("queueBuffer: unknown buffer queued");
@@ -160,6 +173,9 @@
// SurfaceTextureClient currently never queues frames to SurfaceFlinger.
*value = 0;
return NO_ERROR;
+ case NATIVE_WINDOW_CONCRETE_TYPE:
+ *value = NATIVE_WINDOW_SURFACE_TEXTURE_CLIENT;
+ return NO_ERROR;
}
return BAD_VALUE;
}
@@ -189,6 +205,9 @@
case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM:
res = dispatchSetBuffersTransform(args);
break;
+ case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP:
+ res = dispatchSetBuffersTimestamp(args);
+ break;
default:
res = NAME_NOT_FOUND;
break;
@@ -233,6 +252,11 @@
return setBuffersTransform(transform);
}
+int SurfaceTextureClient::dispatchSetBuffersTimestamp(va_list args) {
+ int64_t timestamp = va_arg(args, int64_t);
+ return setBuffersTimestamp(timestamp);
+}
+
int SurfaceTextureClient::connect(int api) {
LOGV("SurfaceTextureClient::connect");
// XXX: Implement this!
@@ -316,6 +340,14 @@
return err;
}
+int SurfaceTextureClient::setBuffersTimestamp(int64_t timestamp)
+{
+ LOGV("SurfaceTextureClient::setBuffersTimestamp");
+ Mutex::Autolock lock(mMutex);
+ mTimestamp = timestamp;
+ return NO_ERROR;
+}
+
void SurfaceTextureClient::freeAllBuffers() {
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
mSlots[i] = 0;
diff --git a/libs/gui/tests/Android.mk b/libs/gui/tests/Android.mk
index 1dd8885..7516299 100644
--- a/libs/gui/tests/Android.mk
+++ b/libs/gui/tests/Android.mk
@@ -4,39 +4,37 @@
ifneq ($(TARGET_SIMULATOR),true)
-# Build the unit tests.
-test_src_files := \
- SurfaceTextureClient_test.cpp \
+LOCAL_MODULE := SurfaceTexture_test
-shared_libraries := \
- libcutils \
- libutils \
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+ SurfaceTextureClient_test.cpp \
+ SurfaceTexture_test.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+ libEGL \
+ libGLESv2 \
+ libandroid \
libbinder \
+ libcutils \
libgui \
libstlport \
+ libsurfaceflinger_client \
+ libui \
+ libutils \
-static_libraries := \
+LOCAL_STATIC_LIBRARIES := \
libgtest \
libgtest_main \
-c_includes := \
+LOCAL_C_INCLUDES := \
bionic \
bionic/libstdc++/include \
external/gtest/include \
external/stlport/stlport \
-module_tags := tests
-
-$(foreach file,$(test_src_files), \
- $(eval include $(CLEAR_VARS)) \
- $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
- $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \
- $(eval LOCAL_C_INCLUDES := $(c_includes)) \
- $(eval LOCAL_SRC_FILES := $(file)) \
- $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
- $(eval LOCAL_MODULE_TAGS := $(module_tags)) \
- $(eval include $(BUILD_EXECUTABLE)) \
-)
+include $(BUILD_EXECUTABLE)
# Build the manual test programs.
include $(call all-subdir-makefiles)
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index 0f140ff..94b05bc 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-#include <gui/SurfaceTextureClient.h>
+#include <EGL/egl.h>
#include <gtest/gtest.h>
+#include <gui/SurfaceTextureClient.h>
namespace android {
@@ -35,6 +36,11 @@
sp<SurfaceTextureClient> mSTC;
};
+TEST_F(SurfaceTextureClientTest, GetISurfaceTextureIsNotNull) {
+ sp<ISurfaceTexture> ist(mSTC->getISurfaceTexture());
+ ASSERT_TRUE(ist != NULL);
+}
+
TEST_F(SurfaceTextureClientTest, QueuesToWindowCompositorIsFalse) {
sp<ANativeWindow> anw(mSTC);
int result = -123;
@@ -44,4 +50,54 @@
EXPECT_EQ(0, result);
}
+TEST_F(SurfaceTextureClientTest, ConcreteTypeIsSurfaceTextureClient) {
+ sp<ANativeWindow> anw(mSTC);
+ int result = -123;
+ int err = anw->query(anw.get(), NATIVE_WINDOW_CONCRETE_TYPE, &result);
+ EXPECT_EQ(NO_ERROR, err);
+ EXPECT_EQ(NATIVE_WINDOW_SURFACE_TEXTURE_CLIENT, result);
+}
+
+TEST_F(SurfaceTextureClientTest, ANativeWindowLockFails) {
+ sp<ANativeWindow> anw(mSTC);
+ ANativeWindow_Buffer buf;
+ ASSERT_EQ(BAD_VALUE, ANativeWindow_lock(anw.get(), &buf, NULL));
+}
+
+TEST_F(SurfaceTextureClientTest, EglCreateWindowSurfaceFails) {
+ sp<ANativeWindow> anw(mSTC);
+
+ EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ ASSERT_NE(EGL_NO_DISPLAY, dpy);
+
+ EGLint majorVersion;
+ EGLint minorVersion;
+ EXPECT_TRUE(eglInitialize(dpy, &majorVersion, &minorVersion));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+ EGLConfig myConfig = {0};
+ EGLint numConfigs = 0;
+ EGLint configAttribs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_ALPHA_SIZE, 8,
+ EGL_DEPTH_SIZE, 16,
+ EGL_STENCIL_SIZE, 8,
+ EGL_NONE };
+ EXPECT_TRUE(eglChooseConfig(dpy, configAttribs, &myConfig, 1,
+ &numConfigs));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+ EGLSurface eglSurface = eglCreateWindowSurface(dpy, myConfig, anw.get(),
+ NULL);
+ ASSERT_EQ(EGL_NO_SURFACE, eglSurface);
+ ASSERT_EQ(EGL_BAD_NATIVE_WINDOW, eglGetError());
+
+ eglTerminate(dpy);
+}
+
}
diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp
new file mode 100644
index 0000000..4184463
--- /dev/null
+++ b/libs/gui/tests/SurfaceTexture_test.cpp
@@ -0,0 +1,621 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <gui/SurfaceTexture.h>
+#include <gui/SurfaceTextureClient.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/String8.h>
+
+#include <surfaceflinger/ISurfaceComposer.h>
+#include <surfaceflinger/Surface.h>
+#include <surfaceflinger/SurfaceComposerClient.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <ui/FramebufferNativeWindow.h>
+
+namespace android {
+
+class GLTest : public ::testing::Test {
+protected:
+
+ GLTest():
+ mEglDisplay(EGL_NO_DISPLAY),
+ mEglSurface(EGL_NO_SURFACE),
+ mEglContext(EGL_NO_CONTEXT) {
+ }
+
+ virtual void SetUp() {
+ EGLBoolean returnValue;
+
+ mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay);
+
+ EGLint majorVersion;
+ EGLint minorVersion;
+ EXPECT_TRUE(eglInitialize(mEglDisplay, &majorVersion, &minorVersion));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ RecordProperty("EglVersionMajor", majorVersion);
+ RecordProperty("EglVersionMajor", minorVersion);
+
+ EGLConfig myConfig = {0};
+ EGLint numConfigs = 0;
+ EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &myConfig,
+ 1, &numConfigs));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+ char* displaySecsEnv = getenv("GLTEST_DISPLAY_SECS");
+ if (displaySecsEnv != NULL) {
+ mDisplaySecs = atoi(displaySecsEnv);
+ if (mDisplaySecs < 0) {
+ mDisplaySecs = 0;
+ }
+ } else {
+ mDisplaySecs = 0;
+ }
+
+ if (mDisplaySecs > 0) {
+ mComposerClient = new SurfaceComposerClient;
+ ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+
+ mSurfaceControl = mComposerClient->createSurface(getpid(),
+ String8("Test Surface"), 0,
+ getSurfaceWidth(), getSurfaceHeight(),
+ PIXEL_FORMAT_RGB_888, 0);
+
+ ASSERT_TRUE(mSurfaceControl != NULL);
+ ASSERT_TRUE(mSurfaceControl->isValid());
+
+ ASSERT_EQ(NO_ERROR, mComposerClient->openTransaction());
+ ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(30000));
+ ASSERT_EQ(NO_ERROR, mSurfaceControl->show());
+ ASSERT_EQ(NO_ERROR, mComposerClient->closeTransaction());
+
+ sp<ANativeWindow> window = mSurfaceControl->getSurface();
+ mEglSurface = eglCreateWindowSurface(mEglDisplay, myConfig,
+ window.get(), NULL);
+ } else {
+ EGLint pbufferAttribs[] = {
+ EGL_WIDTH, getSurfaceWidth(),
+ EGL_HEIGHT, getSurfaceHeight(),
+ EGL_NONE };
+
+ mEglSurface = eglCreatePbufferSurface(mEglDisplay, myConfig,
+ pbufferAttribs);
+ }
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ ASSERT_NE(EGL_NO_SURFACE, mEglSurface);
+
+ mEglContext = eglCreateContext(mEglDisplay, myConfig, EGL_NO_CONTEXT,
+ getContextAttribs());
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ ASSERT_NE(EGL_NO_CONTEXT, mEglContext);
+
+ EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
+ mEglContext));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+ EGLint w, h;
+ EXPECT_TRUE(eglQuerySurface(mEglDisplay, mEglSurface, EGL_WIDTH, &w));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ EXPECT_TRUE(eglQuerySurface(mEglDisplay, mEglSurface, EGL_HEIGHT, &h));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ RecordProperty("EglSurfaceWidth", w);
+ RecordProperty("EglSurfaceHeight", h);
+
+ glViewport(0, 0, w, h);
+ ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+ }
+
+ virtual void TearDown() {
+ // Display the result
+ if (mDisplaySecs > 0 && mEglSurface != EGL_NO_SURFACE) {
+ eglSwapBuffers(mEglDisplay, mEglSurface);
+ sleep(mDisplaySecs);
+ }
+
+ if (mComposerClient != NULL) {
+ mComposerClient->dispose();
+ }
+ if (mEglContext != EGL_NO_CONTEXT) {
+ eglDestroyContext(mEglDisplay, mEglContext);
+ }
+ if (mEglSurface != EGL_NO_SURFACE) {
+ eglDestroySurface(mEglDisplay, mEglSurface);
+ }
+ if (mEglDisplay != EGL_NO_DISPLAY) {
+ eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
+ EGL_NO_CONTEXT);
+ eglTerminate(mEglDisplay);
+ }
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ }
+
+ virtual EGLint const* getConfigAttribs() {
+ static EGLint sDefaultConfigAttribs[] = {
+ EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_ALPHA_SIZE, 8,
+ EGL_DEPTH_SIZE, 16,
+ EGL_STENCIL_SIZE, 8,
+ EGL_NONE };
+
+ return sDefaultConfigAttribs;
+ }
+
+ virtual EGLint const* getContextAttribs() {
+ static EGLint sDefaultContextAttribs[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE };
+
+ return sDefaultContextAttribs;
+ }
+
+ virtual EGLint getSurfaceWidth() {
+ return 64;
+ }
+
+ virtual EGLint getSurfaceHeight() {
+ return 64;
+ }
+
+ void loadShader(GLenum shaderType, const char* pSource, GLuint* outShader) {
+ GLuint shader = glCreateShader(shaderType);
+ ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+ if (shader) {
+ glShaderSource(shader, 1, &pSource, NULL);
+ ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+ glCompileShader(shader);
+ ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+ GLint compiled = 0;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+ ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+ if (!compiled) {
+ GLint infoLen = 0;
+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
+ ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+ if (infoLen) {
+ char* buf = (char*) malloc(infoLen);
+ if (buf) {
+ glGetShaderInfoLog(shader, infoLen, NULL, buf);
+ printf("Shader compile log:\n%s\n", buf);
+ free(buf);
+ FAIL();
+ }
+ } else {
+ char* buf = (char*) malloc(0x1000);
+ if (buf) {
+ glGetShaderInfoLog(shader, 0x1000, NULL, buf);
+ printf("Shader compile log:\n%s\n", buf);
+ free(buf);
+ FAIL();
+ }
+ }
+ glDeleteShader(shader);
+ shader = 0;
+ }
+ }
+ ASSERT_TRUE(shader != 0);
+ *outShader = shader;
+ }
+
+ void createProgram(const char* pVertexSource, const char* pFragmentSource,
+ GLuint* outPgm) {
+ GLuint vertexShader, fragmentShader;
+ {
+ SCOPED_TRACE("compiling vertex shader");
+ loadShader(GL_VERTEX_SHADER, pVertexSource, &vertexShader);
+ if (HasFatalFailure()) {
+ return;
+ }
+ }
+ {
+ SCOPED_TRACE("compiling fragment shader");
+ loadShader(GL_FRAGMENT_SHADER, pFragmentSource, &fragmentShader);
+ if (HasFatalFailure()) {
+ return;
+ }
+ }
+
+ GLuint program = glCreateProgram();
+ ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+ if (program) {
+ glAttachShader(program, vertexShader);
+ ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+ glAttachShader(program, fragmentShader);
+ ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+ glLinkProgram(program);
+ GLint linkStatus = GL_FALSE;
+ glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
+ if (linkStatus != GL_TRUE) {
+ GLint bufLength = 0;
+ glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
+ if (bufLength) {
+ char* buf = (char*) malloc(bufLength);
+ if (buf) {
+ glGetProgramInfoLog(program, bufLength, NULL, buf);
+ printf("Program link log:\n%s\n", buf);
+ free(buf);
+ FAIL();
+ }
+ }
+ glDeleteProgram(program);
+ program = 0;
+ }
+ }
+ glDeleteShader(vertexShader);
+ glDeleteShader(fragmentShader);
+ ASSERT_TRUE(program != 0);
+ *outPgm = program;
+ }
+
+ ::testing::AssertionResult checkPixel(int x, int y, int r,
+ int g, int b, int a) {
+ GLubyte pixel[4];
+ String8 msg;
+ glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
+ GLenum err = glGetError();
+ if (err != GL_NO_ERROR) {
+ msg += String8::format("error reading pixel: %#x", err);
+ while ((err = glGetError()) != GL_NO_ERROR) {
+ msg += String8::format(", %#x", err);
+ }
+ fprintf(stderr, "pixel check failure: %s\n", msg.string());
+ return ::testing::AssertionFailure(
+ ::testing::Message(msg.string()));
+ }
+ if (r >= 0 && GLubyte(r) != pixel[0]) {
+ msg += String8::format("r(%d isn't %d)", pixel[0], r);
+ }
+ if (g >= 0 && GLubyte(g) != pixel[1]) {
+ if (!msg.isEmpty()) {
+ msg += " ";
+ }
+ msg += String8::format("g(%d isn't %d)", pixel[1], g);
+ }
+ if (b >= 0 && GLubyte(b) != pixel[2]) {
+ if (!msg.isEmpty()) {
+ msg += " ";
+ }
+ msg += String8::format("b(%d isn't %d)", pixel[2], b);
+ }
+ if (a >= 0 && GLubyte(a) != pixel[3]) {
+ if (!msg.isEmpty()) {
+ msg += " ";
+ }
+ msg += String8::format("a(%d isn't %d)", pixel[3], a);
+ }
+ if (!msg.isEmpty()) {
+ fprintf(stderr, "pixel check failure: %s\n", msg.string());
+ return ::testing::AssertionFailure(
+ ::testing::Message(msg.string()));
+ } else {
+ return ::testing::AssertionSuccess();
+ }
+ }
+
+ int mDisplaySecs;
+ sp<SurfaceComposerClient> mComposerClient;
+ sp<SurfaceControl> mSurfaceControl;
+
+ EGLDisplay mEglDisplay;
+ EGLSurface mEglSurface;
+ EGLContext mEglContext;
+};
+
+// XXX: Code above this point should live elsewhere
+
+class SurfaceTextureGLTest : public GLTest {
+protected:
+ static const GLint TEX_ID = 123;
+
+ virtual void SetUp() {
+ GLTest::SetUp();
+ mST = new SurfaceTexture(TEX_ID);
+ mSTC = new SurfaceTextureClient(mST);
+ mANW = mSTC;
+
+ const char vsrc[] =
+ "attribute vec4 vPosition;\n"
+ "varying vec2 texCoords;\n"
+ "uniform mat4 texMatrix;\n"
+ "void main() {\n"
+ " vec2 vTexCoords = 0.5 * (vPosition.xy + vec2(1.0, 1.0));\n"
+ " texCoords = (texMatrix * vec4(vTexCoords, 0.0, 1.0)).xy;\n"
+ " gl_Position = vPosition;\n"
+ "}\n";
+
+ const char fsrc[] =
+ "#extension GL_OES_EGL_image_external : require\n"
+ "precision mediump float;\n"
+ "uniform samplerExternalOES texSampler;\n"
+ "varying vec2 texCoords;\n"
+ "void main() {\n"
+ " gl_FragColor = texture2D(texSampler, texCoords);\n"
+ "}\n";
+
+ {
+ SCOPED_TRACE("creating shader program");
+ createProgram(vsrc, fsrc, &mPgm);
+ if (HasFatalFailure()) {
+ return;
+ }
+ }
+
+ mPositionHandle = glGetAttribLocation(mPgm, "vPosition");
+ ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+ ASSERT_NE(-1, mPositionHandle);
+ mTexSamplerHandle = glGetUniformLocation(mPgm, "texSampler");
+ ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+ ASSERT_NE(-1, mTexSamplerHandle);
+ mTexMatrixHandle = glGetUniformLocation(mPgm, "texMatrix");
+ ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+ ASSERT_NE(-1, mTexMatrixHandle);
+ }
+
+ // drawTexture draws the SurfaceTexture over the entire GL viewport.
+ void drawTexture() {
+ const GLfloat triangleVertices[] = {
+ -1.0f, 1.0f,
+ -1.0f, -1.0f,
+ 1.0f, -1.0f,
+ 1.0f, 1.0f,
+ };
+
+ glVertexAttribPointer(mPositionHandle, 2, GL_FLOAT, GL_FALSE, 0, triangleVertices);
+ ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+ glEnableVertexAttribArray(mPositionHandle);
+ ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+
+ glUseProgram(mPgm);
+ glUniform1i(mTexSamplerHandle, 0);
+ ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+ glBindTexture(GL_TEXTURE_EXTERNAL_OES, TEX_ID);
+ ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+
+ GLfloat texMatrix[16];
+ mST->getTransformMatrix(texMatrix);
+ glUniformMatrix4fv(mTexMatrixHandle, 1, GL_FALSE, texMatrix);
+
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+ }
+
+ sp<SurfaceTexture> mST;
+ sp<SurfaceTextureClient> mSTC;
+ sp<ANativeWindow> mANW;
+
+ GLuint mPgm;
+ GLint mPositionHandle;
+ GLint mTexSamplerHandle;
+ GLint mTexMatrixHandle;
+};
+
+// Fill a YV12 buffer with a multi-colored checkerboard pattern
+void fillYV12Buffer(uint8_t* buf, int w, int h, int stride) {
+ const int blockWidth = w > 16 ? w / 16 : 1;
+ const int blockHeight = h > 16 ? h / 16 : 1;
+ const int yuvTexOffsetY = 0;
+ int yuvTexStrideY = stride;
+ int yuvTexOffsetV = yuvTexStrideY * h;
+ int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf;
+ int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * h/2;
+ int yuvTexStrideU = yuvTexStrideV;
+ for (int x = 0; x < w; x++) {
+ for (int y = 0; y < h; y++) {
+ int parityX = (x / blockWidth) & 1;
+ int parityY = (y / blockHeight) & 1;
+ unsigned char intensity = (parityX ^ parityY) ? 63 : 191;
+ buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = intensity;
+ if (x < w / 2 && y < h / 2) {
+ buf[yuvTexOffsetU + (y * yuvTexStrideU) + x] = intensity;
+ if (x * 2 < w / 2 && y * 2 < h / 2) {
+ buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 0] =
+ buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 1] =
+ buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 0] =
+ buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 1] =
+ intensity;
+ }
+ }
+ }
+ }
+}
+
+// Fill a YV12 buffer with red outside a given rectangle and green inside it.
+void fillYV12BufferRect(uint8_t* buf, int w, int h, int stride,
+ const android_native_rect_t& rect) {
+ const int yuvTexOffsetY = 0;
+ int yuvTexStrideY = stride;
+ int yuvTexOffsetV = yuvTexStrideY * h;
+ int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf;
+ int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * h/2;
+ int yuvTexStrideU = yuvTexStrideV;
+ for (int x = 0; x < w; x++) {
+ for (int y = 0; y < h; y++) {
+ bool inside = rect.left <= x && x < rect.right &&
+ rect.top <= y && y < rect.bottom;
+ buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = inside ? 240 : 64;
+ if (x < w / 2 && y < h / 2) {
+ bool inside = rect.left <= 2*x && 2*x < rect.right &&
+ rect.top <= 2*y && 2*y < rect.bottom;
+ buf[yuvTexOffsetU + (y * yuvTexStrideU) + x] = 16;
+ buf[yuvTexOffsetV + (y * yuvTexStrideV) + x] =
+ inside ? 16 : 255;
+ }
+ }
+ }
+}
+
+TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferNpot) {
+ const int yuvTexWidth = 64;
+ const int yuvTexHeight = 66;
+
+ ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(),
+ yuvTexWidth, yuvTexHeight, HAL_PIXEL_FORMAT_YV12));
+ ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
+ GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN));
+
+ android_native_buffer_t* anb;
+ ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
+ ASSERT_TRUE(anb != NULL);
+
+ sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
+ ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer()));
+
+ // Fill the buffer with the a checkerboard pattern
+ uint8_t* img = NULL;
+ buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
+ fillYV12Buffer(img, yuvTexWidth, yuvTexHeight, buf->getStride());
+ buf->unlock();
+ ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer()));
+
+ mST->updateTexImage();
+
+ glClearColor(0.2, 0.2, 0.2, 0.2);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ drawTexture();
+
+ EXPECT_TRUE(checkPixel( 0, 0, 255, 127, 255, 255));
+ EXPECT_TRUE(checkPixel(63, 0, 0, 133, 0, 255));
+ EXPECT_TRUE(checkPixel(63, 63, 0, 133, 0, 255));
+ EXPECT_TRUE(checkPixel( 0, 63, 255, 127, 255, 255));
+
+ EXPECT_TRUE(checkPixel(22, 44, 247, 70, 255, 255));
+ EXPECT_TRUE(checkPixel(45, 52, 209, 32, 235, 255));
+ EXPECT_TRUE(checkPixel(52, 51, 100, 255, 73, 255));
+ EXPECT_TRUE(checkPixel( 7, 31, 155, 0, 118, 255));
+ EXPECT_TRUE(checkPixel(31, 9, 148, 71, 110, 255));
+ EXPECT_TRUE(checkPixel(29, 35, 255, 127, 255, 255));
+ EXPECT_TRUE(checkPixel(36, 22, 155, 29, 0, 255));
+}
+
+// XXX: This test is disabled because it it currently broken on all devices to
+// which I have access. Some of the checkPixel calls are not correct because
+// I just copied them from the npot test above and haven't bothered to figure
+// out the correct values.
+TEST_F(SurfaceTextureGLTest, DISABLED_TexturingFromCpuFilledYV12BufferPow2) {
+ const int yuvTexWidth = 64;
+ const int yuvTexHeight = 64;
+
+ ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(),
+ yuvTexWidth, yuvTexHeight, HAL_PIXEL_FORMAT_YV12));
+ ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
+ GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN));
+
+ android_native_buffer_t* anb;
+ ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
+ ASSERT_TRUE(anb != NULL);
+
+ sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
+ ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer()));
+
+ // Fill the buffer with the a checkerboard pattern
+ uint8_t* img = NULL;
+ buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
+ fillYV12Buffer(img, yuvTexWidth, yuvTexHeight, buf->getStride());
+ buf->unlock();
+ ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer()));
+
+ mST->updateTexImage();
+
+ glClearColor(0.2, 0.2, 0.2, 0.2);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ drawTexture();
+
+ EXPECT_TRUE(checkPixel( 0, 0, 255, 127, 255, 255));
+ EXPECT_TRUE(checkPixel(63, 0, 0, 133, 0, 255));
+ EXPECT_TRUE(checkPixel(63, 63, 0, 133, 0, 255));
+ EXPECT_TRUE(checkPixel( 0, 63, 255, 127, 255, 255));
+
+ EXPECT_TRUE(checkPixel(22, 19, 247, 70, 255, 255));
+ EXPECT_TRUE(checkPixel(45, 11, 209, 32, 235, 255));
+ EXPECT_TRUE(checkPixel(52, 12, 100, 255, 73, 255));
+ EXPECT_TRUE(checkPixel( 7, 32, 155, 0, 118, 255));
+ EXPECT_TRUE(checkPixel(31, 54, 148, 71, 110, 255));
+ EXPECT_TRUE(checkPixel(29, 28, 255, 127, 255, 255));
+ EXPECT_TRUE(checkPixel(36, 41, 155, 29, 0, 255));
+}
+
+TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) {
+ const int yuvTexWidth = 64;
+ const int yuvTexHeight = 66;
+
+ ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(),
+ yuvTexWidth, yuvTexHeight, HAL_PIXEL_FORMAT_YV12));
+ ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
+ GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN));
+
+ android_native_rect_t crops[] = {
+ {4, 6, 22, 36},
+ {0, 6, 22, 36},
+ {4, 0, 22, 36},
+ {4, 6, yuvTexWidth, 36},
+ {4, 6, 22, yuvTexHeight},
+ };
+
+ for (int i = 0; i < 5; i++) {
+ const android_native_rect_t& crop(crops[i]);
+ SCOPED_TRACE(String8::format("rect{ l: %d t: %d r: %d b: %d }", crop.left,
+ crop.top, crop.right, crop.bottom).string());
+
+ ASSERT_EQ(NO_ERROR, native_window_set_crop(mANW.get(), &crop));
+
+ android_native_buffer_t* anb;
+ ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
+ ASSERT_TRUE(anb != NULL);
+
+ sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
+ ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer()));
+
+ uint8_t* img = NULL;
+ buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
+ fillYV12BufferRect(img, yuvTexWidth, yuvTexHeight, buf->getStride(), crop);
+ buf->unlock();
+ ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer()));
+
+ mST->updateTexImage();
+
+ glClearColor(0.2, 0.2, 0.2, 0.2);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ drawTexture();
+
+ EXPECT_TRUE(checkPixel( 0, 0, 82, 255, 35, 255));
+ EXPECT_TRUE(checkPixel(63, 0, 82, 255, 35, 255));
+ EXPECT_TRUE(checkPixel(63, 63, 82, 255, 35, 255));
+ EXPECT_TRUE(checkPixel( 0, 63, 82, 255, 35, 255));
+
+ EXPECT_TRUE(checkPixel(25, 14, 82, 255, 35, 255));
+ EXPECT_TRUE(checkPixel(35, 31, 82, 255, 35, 255));
+ EXPECT_TRUE(checkPixel(57, 6, 82, 255, 35, 255));
+ EXPECT_TRUE(checkPixel( 5, 42, 82, 255, 35, 255));
+ EXPECT_TRUE(checkPixel(32, 33, 82, 255, 35, 255));
+ EXPECT_TRUE(checkPixel(16, 26, 82, 255, 35, 255));
+ EXPECT_TRUE(checkPixel(46, 51, 82, 255, 35, 255));
+ }
+}
+
+}
diff --git a/libs/surfaceflinger_client/Surface.cpp b/libs/surfaceflinger_client/Surface.cpp
index afabbf4..0dfbf01 100644
--- a/libs/surfaceflinger_client/Surface.cpp
+++ b/libs/surfaceflinger_client/Surface.cpp
@@ -712,11 +712,15 @@
case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
*value = MIN_UNDEQUEUED_BUFFERS;
return NO_ERROR;
- case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER:
+ case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: {
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
*value = sf->authenticateSurface(mSurface) ? 1 : 0;
return NO_ERROR;
}
+ case NATIVE_WINDOW_CONCRETE_TYPE:
+ *value = NATIVE_WINDOW_SURFACE;
+ return NO_ERROR;
+ }
return BAD_VALUE;
}
@@ -749,6 +753,9 @@
case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM:
res = dispatch_set_buffers_transform( args );
break;
+ case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP:
+ res = dispatch_set_buffers_timestamp( args );
+ break;
default:
res = NAME_NOT_FOUND;
break;
@@ -788,6 +795,11 @@
return setBuffersTransform(transform);
}
+int Surface::dispatch_set_buffers_timestamp(va_list args) {
+ int64_t timestamp = va_arg(args, int64_t);
+ return setBuffersTimestamp(timestamp);
+}
+
void Surface::setUsage(uint32_t reqUsage)
{
Mutex::Autolock _l(mSurfaceLock);
@@ -906,6 +918,13 @@
return NO_ERROR;
}
+int Surface::setBuffersTimestamp(int64_t timestamp)
+{
+ // Surface doesn't really have anything meaningful to do with timestamps
+ // so they'll just be dropped here.
+ return NO_ERROR;
+}
+
// ----------------------------------------------------------------------------
int Surface::getConnectedApi() const
diff --git a/libs/surfaceflinger_client/tests/Surface_test.cpp b/libs/surfaceflinger_client/tests/Surface_test.cpp
index 74ebf4e..fd07479 100644
--- a/libs/surfaceflinger_client/tests/Surface_test.cpp
+++ b/libs/surfaceflinger_client/tests/Surface_test.cpp
@@ -130,4 +130,12 @@
ASSERT_TRUE(heap != NULL);
}
+TEST_F(SurfaceTest, ConcreteTypeIsSurface) {
+ sp<ANativeWindow> anw(mSurface);
+ int result = -123;
+ int err = anw->query(anw.get(), NATIVE_WINDOW_CONCRETE_TYPE, &result);
+ EXPECT_EQ(NO_ERROR, err);
+ EXPECT_EQ(NATIVE_WINDOW_SURFACE, result);
+}
+
}
diff --git a/libs/ui/FramebufferNativeWindow.cpp b/libs/ui/FramebufferNativeWindow.cpp
index 0702d49..dc223f9 100644
--- a/libs/ui/FramebufferNativeWindow.cpp
+++ b/libs/ui/FramebufferNativeWindow.cpp
@@ -286,6 +286,9 @@
case NATIVE_WINDOW_FORMAT:
*value = fb->format;
return NO_ERROR;
+ case NATIVE_WINDOW_CONCRETE_TYPE:
+ *value = NATIVE_WINDOW_FRAMEBUFFER;
+ return NO_ERROR;
}
*value = 0;
return BAD_VALUE;
diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp
index eaa8926..440ec00 100644
--- a/libs/ui/Input.cpp
+++ b/libs/ui/Input.cpp
@@ -469,6 +469,16 @@
return value;
}
+ssize_t MotionEvent::findPointerIndex(int32_t pointerId) const {
+ size_t pointerCount = mPointerIds.size();
+ for (size_t i = 0; i < pointerCount; i++) {
+ if (mPointerIds.itemAt(i) == pointerId) {
+ return i;
+ }
+ }
+ return -1;
+}
+
void MotionEvent::offsetLocation(float xOffset, float yOffset) {
mXOffset += xOffset;
mYOffset += yOffset;
@@ -668,12 +678,27 @@
void VelocityTracker::clear() {
mIndex = 0;
mMovements[0].idBits.clear();
+ mActivePointerId = -1;
+}
+
+void VelocityTracker::clearPointers(BitSet32 idBits) {
+ BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value);
+ mMovements[mIndex].idBits = remainingIdBits;
+
+ if (mActivePointerId >= 0 && idBits.hasBit(mActivePointerId)) {
+ mActivePointerId = !remainingIdBits.isEmpty() ? remainingIdBits.firstMarkedBit() : -1;
+ }
}
void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions) {
if (++mIndex == HISTORY_SIZE) {
mIndex = 0;
}
+
+ while (idBits.count() > MAX_POINTERS) {
+ idBits.clearBit(idBits.lastMarkedBit());
+ }
+
Movement& movement = mMovements[mIndex];
movement.eventTime = eventTime;
movement.idBits = idBits;
@@ -682,8 +707,13 @@
movement.positions[i] = positions[i];
}
+ if (mActivePointerId < 0 || !idBits.hasBit(mActivePointerId)) {
+ mActivePointerId = count != 0 ? idBits.firstMarkedBit() : -1;
+ }
+
#if DEBUG_VELOCITY
- LOGD("VelocityTracker: addMovement eventTime=%lld, idBits=0x%08x", eventTime, idBits.value);
+ LOGD("VelocityTracker: addMovement eventTime=%lld, idBits=0x%08x, activePointerId=%d",
+ eventTime, idBits.value, mActivePointerId);
for (BitSet32 iterBits(idBits); !iterBits.isEmpty(); ) {
uint32_t id = iterBits.firstMarkedBit();
uint32_t index = idBits.getIndexOfBit(id);
@@ -691,7 +721,7 @@
float vx, vy;
bool available = getVelocity(id, &vx, &vy);
if (available) {
- LOGD(" %d: position (%0.3f, %0.3f), velocity (%0.3f, %0.3f), speed %0.3f",
+ LOGD(" %d: position (%0.3f, %0.3f), vx=%0.3f, vy=%0.3f, speed=%0.3f",
id, positions[index].x, positions[index].y, vx, vy, sqrtf(vx * vx + vy * vy));
} else {
assert(vx == 0 && vy == 0);
@@ -702,6 +732,70 @@
#endif
}
+void VelocityTracker::addMovement(const MotionEvent* event) {
+ int32_t actionMasked = event->getActionMasked();
+
+ switch (actionMasked) {
+ case AMOTION_EVENT_ACTION_DOWN:
+ // Clear all pointers on down before adding the new movement.
+ clear();
+ break;
+ case AMOTION_EVENT_ACTION_POINTER_DOWN: {
+ // Start a new movement trace for a pointer that just went down.
+ // We do this on down instead of on up because the client may want to query the
+ // final velocity for a pointer that just went up.
+ BitSet32 downIdBits;
+ downIdBits.markBit(event->getActionIndex());
+ clearPointers(downIdBits);
+ break;
+ }
+ case AMOTION_EVENT_ACTION_OUTSIDE:
+ case AMOTION_EVENT_ACTION_CANCEL:
+ case AMOTION_EVENT_ACTION_SCROLL:
+ case AMOTION_EVENT_ACTION_UP:
+ case AMOTION_EVENT_ACTION_POINTER_UP:
+ // Ignore these actions because they do not convey any new information about
+ // pointer movement. We also want to preserve the last known velocity of the pointers.
+ // Note that ACTION_UP and ACTION_POINTER_UP always report the last known position
+ // of the pointers that went up. ACTION_POINTER_UP does include the new position of
+ // pointers that remained down but we will also receive an ACTION_MOVE with this
+ // information if any of them actually moved. Since we don't know how many pointers
+ // will be going up at once it makes sense to just wait for the following ACTION_MOVE
+ // before adding the movement.
+ return;
+ }
+
+ size_t pointerCount = event->getPointerCount();
+ if (pointerCount > MAX_POINTERS) {
+ pointerCount = MAX_POINTERS;
+ }
+
+ BitSet32 idBits;
+ for (size_t i = 0; i < pointerCount; i++) {
+ idBits.markBit(event->getPointerId(i));
+ }
+
+ nsecs_t eventTime;
+ Position positions[pointerCount];
+
+ size_t historySize = event->getHistorySize();
+ for (size_t h = 0; h < historySize; h++) {
+ eventTime = event->getHistoricalEventTime(h);
+ for (size_t i = 0; i < pointerCount; i++) {
+ positions[i].x = event->getHistoricalX(i, h);
+ positions[i].y = event->getHistoricalY(i, h);
+ }
+ addMovement(eventTime, idBits, positions);
+ }
+
+ eventTime = event->getEventTime();
+ for (size_t i = 0; i < pointerCount; i++) {
+ positions[i].x = event->getX(i);
+ positions[i].y = event->getY(i);
+ }
+ addMovement(eventTime, idBits, positions);
+}
+
bool VelocityTracker::getVelocity(uint32_t id, float* outVx, float* outVy) const {
const Movement& newestMovement = mMovements[mIndex];
if (newestMovement.idBits.hasBit(id)) {
@@ -719,36 +813,17 @@
oldestIndex = nextOldestIndex;
} while (++numTouches < HISTORY_SIZE);
- // If we have a lot of samples, skip the last received sample since it is
- // probably pretty noisy compared to the sum of all of the traces already acquired.
+ // Calculate an exponentially weighted moving average of the velocity estimate
+ // at different points in time measured relative to the oldest sample.
+ // This is essentially an IIR filter. Newer samples are weighted more heavily
+ // than older samples. Samples at equal time points are weighted more or less
+ // equally.
//
- // NOTE: This condition exists in the android.view.VelocityTracker and imposes a
- // bias against the most recent data.
- if (numTouches > 3) {
- numTouches -= 1;
- }
-
- // Calculate an exponentially weighted moving average of the velocity at different
- // points in time measured relative to the oldest samples. This is essentially
- // an IIR filter.
- //
- // One problem with this algorithm is that the sample data may be poorly conditioned.
+ // One tricky problem is that the sample data may be poorly conditioned.
// Sometimes samples arrive very close together in time which can cause us to
// overestimate the velocity at that time point. Most samples might be measured
- // 16ms apart but some consecutive samples could be only 0.5sm apart due to
- // the way they are reported by the hardware or driver (sometimes in bursts or with
- // significant jitter). The instantaneous velocity for those samples 0.5ms apart will
- // be calculated to be 32 times what it should have been.
- // To work around this effect, we impose a minimum duration on the samples.
- //
- // FIXME: Samples close together in time can have an disproportionately large
- // impact on the result because all samples are equally weighted. The average should
- // instead take the time factor into account.
- //
- // FIXME: The minimum duration condition does not exist in
- // android.view.VelocityTracker yet. It is less important there because sample times
- // are truncated to the millisecond so back to back samples will often appear to be
- // zero milliseconds apart and will be ignored if they are the oldest ones.
+ // 16ms apart but some consecutive samples could be only 0.5sm apart because
+ // the hardware or driver reports them irregularly or in bursts.
float accumVx = 0;
float accumVy = 0;
uint32_t index = oldestIndex;
@@ -756,19 +831,27 @@
const Movement& oldestMovement = mMovements[oldestIndex];
const Position& oldestPosition =
oldestMovement.positions[oldestMovement.idBits.getIndexOfBit(id)];
+ nsecs_t lastDuration = 0;
while (numTouches-- > 1) {
if (++index == HISTORY_SIZE) {
index = 0;
}
const Movement& movement = mMovements[index];
nsecs_t duration = movement.eventTime - oldestMovement.eventTime;
- if (duration > MIN_DURATION) {
+
+ // If the duration between samples is small, we may significantly overestimate
+ // the velocity. Consequently, we impose a minimum duration constraint on the
+ // samples that we include in the calculation.
+ if (duration >= MIN_DURATION) {
const Position& position = movement.positions[movement.idBits.getIndexOfBit(id)];
float scale = 1000000000.0f / duration; // one over time delta in seconds
float vx = (position.x - oldestPosition.x) * scale;
float vy = (position.y - oldestPosition.y) * scale;
- accumVx = accumVx == 0 ? vx : (accumVx + vx) * 0.5f;
- accumVy = accumVy == 0 ? vy : (accumVy + vy) * 0.5f;
+
+ accumVx = (accumVx * lastDuration + vx * duration) / (duration + lastDuration);
+ accumVy = (accumVy * lastDuration + vy * duration) / (duration + lastDuration);
+
+ lastDuration = duration;
samplesUsed += 1;
}
}
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index 1994f6a..a060a5f 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -56,6 +56,9 @@
Region::Region(const Region& rhs)
: mBounds(rhs.mBounds), mStorage(rhs.mStorage)
{
+#if VALIDATE_REGIONS
+ validate(rhs, "rhs copy-ctor");
+#endif
}
Region::Region(const Rect& rhs)
@@ -76,7 +79,8 @@
Region& Region::operator = (const Region& rhs)
{
#if VALIDATE_REGIONS
- validate(rhs, "operator=");
+ validate(*this, "this->operator=");
+ validate(rhs, "rhs.operator=");
#endif
mBounds = rhs.mBounds;
mStorage = rhs.mStorage;
@@ -366,6 +370,12 @@
const Region& lhs,
const Region& rhs, int dx, int dy)
{
+#if VALIDATE_REGIONS
+ validate(lhs, "boolean_operation (before): lhs");
+ validate(rhs, "boolean_operation (before): rhs");
+ validate(dst, "boolean_operation (before): dst");
+#endif
+
size_t lhs_count;
Rect const * const lhs_rects = lhs.getArray(&lhs_count);
diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp
index 18f858b..d5dd126 100644
--- a/libs/utils/Looper.cpp
+++ b/libs/utils/Looper.cpp
@@ -218,14 +218,10 @@
// Adjust the timeout based on when the next message is due.
if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- if (mNextMessageUptime <= now) {
- timeoutMillis = 0;
- } else {
- uint64_t delay = (mNextMessageUptime - now + 999999LL) / 1000000LL;
- if (delay < INT_MAX
- && (timeoutMillis < 0 || int(delay) < timeoutMillis)) {
- timeoutMillis = int(delay);
- }
+ int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime);
+ if (messageTimeoutMillis >= 0
+ && (timeoutMillis < 0 || messageTimeoutMillis < timeoutMillis)) {
+ timeoutMillis = messageTimeoutMillis;
}
#if DEBUG_POLL_AND_WAKE
LOGD("%p ~ pollOnce - next message in %lldns, adjusted timeout: timeoutMillis=%d",
@@ -444,12 +440,11 @@
return result;
}
- nsecs_t timeoutNanos = endTime - systemTime(SYSTEM_TIME_MONOTONIC);
- if (timeoutNanos <= 0) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ timeoutMillis = toMillisecondTimeoutDelay(now, endTime);
+ if (timeoutMillis == 0) {
return ALOOPER_POLL_TIMEOUT;
}
-
- timeoutMillis = int(nanoseconds_to_milliseconds(timeoutNanos + 999999LL));
}
}
}
diff --git a/libs/utils/Timers.cpp b/libs/utils/Timers.cpp
index 784f035..64a29f5 100644
--- a/libs/utils/Timers.cpp
+++ b/libs/utils/Timers.cpp
@@ -26,6 +26,7 @@
#include <sys/time.h>
#include <time.h>
#include <errno.h>
+#include <limits.h>
#ifdef HAVE_WIN32_THREADS
#include <windows.h>
@@ -53,6 +54,23 @@
#endif
}
+int toMillisecondTimeoutDelay(nsecs_t referenceTime, nsecs_t timeoutTime)
+{
+ int timeoutDelayMillis;
+ if (timeoutTime > referenceTime) {
+ uint64_t timeoutDelay = uint64_t(timeoutTime - referenceTime);
+ if (timeoutDelay > uint64_t((INT_MAX - 1) * 1000000LL)) {
+ timeoutDelayMillis = -1;
+ } else {
+ timeoutDelayMillis = (timeoutDelay + 999999LL) / 1000000LL;
+ }
+ } else {
+ timeoutDelayMillis = 0;
+ }
+ return timeoutDelayMillis;
+}
+
+
/*
* ===========================================================================
* DurationTimer
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 861d7ac..75f7078 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -340,7 +340,10 @@
}
if (gEGLDebugLevel > 0)
- StartDebugServer();
+ {
+ property_get("debug.egl.debug_port", value, "5039");
+ StartDebugServer(atoi(value));
+ }
}
static void setGLHooksThreadSpecific(gl_hooks_t const *value) {
@@ -350,7 +353,6 @@
} else if (gEGLDebugLevel > 0 && value != &gHooksNoContext) {
setGlTraceThreadSpecific(value);
setGlThreadSpecific(&gHooksDebug);
- LOGD("\n* setGLHooksThreadSpecific gHooksDebug");
} else {
setGlThreadSpecific(value);
}
@@ -1130,6 +1132,16 @@
EGLConfig iConfig = dp->configs[intptr_t(config)].config;
EGLint format;
+ // for now fail if the window is not a Surface.
+ int type = -1;
+ ANativeWindow* anw = reinterpret_cast<ANativeWindow*>(window);
+ if ((anw->query(window, NATIVE_WINDOW_CONCRETE_TYPE, &type) != 0) ||
+ (type == NATIVE_WINDOW_SURFACE_TEXTURE_CLIENT)) {
+ LOGE("native window is a SurfaceTextureClient (currently "
+ "unsupported)");
+ return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
+ }
+
// set the native window's buffers format to match this config
if (cnx->egl.eglGetConfigAttrib(iDpy,
iConfig, EGL_NATIVE_VISUAL_ID, &format)) {
@@ -2109,14 +2121,15 @@
if (!validate_display_context(dpy, ctx))
return EGL_FALSE;
+ EGLBoolean result = EGL_FALSE;
egl_context_t * const c = get_context(ctx);
-
if (c->cnx->egl.eglDestroySyncKHR) {
- return c->cnx->egl.eglDestroySyncKHR(
+ result = c->cnx->egl.eglDestroySyncKHR(
dp->disp[c->impl].dpy, syncObject->sync);
+ if (result)
+ _s.terminate();
}
-
- return EGL_FALSE;
+ return result;
}
EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout)
diff --git a/opengl/libs/GLES2_dbg/generate_debugger_message_proto.py b/opengl/libs/GLES2_dbg/generate_debugger_message_proto.py
index b14885b..48a29da 100755
--- a/opengl/libs/GLES2_dbg/generate_debugger_message_proto.py
+++ b/opengl/libs/GLES2_dbg/generate_debugger_message_proto.py
@@ -98,9 +98,6 @@
output.write(" SETPROP = %d;\n" % (i))
i += 1
- output.write(" CAPTURE = %d;\n" % (i))
- i += 1
-
output.write(""" }
required Function function = 2 [default = NEG]; // type/function of message
enum Type
diff --git a/opengl/libs/GLES2_dbg/src/dbgcontext.cpp b/opengl/libs/GLES2_dbg/src/dbgcontext.cpp
index 68514df..5c41825 100644
--- a/opengl/libs/GLES2_dbg/src/dbgcontext.cpp
+++ b/opengl/libs/GLES2_dbg/src/dbgcontext.cpp
@@ -65,8 +65,9 @@
void DbgContext::glUseProgram(GLuint program)
{
- assert(GL_NO_ERROR == hooks->gl.glGetError());
-
+ while (GLenum error = hooks->gl.glGetError())
+ LOGD("DbgContext::glUseProgram: before glGetError() = 0x%.4X", error);
+
this->program = program;
GLint activeAttributes = 0;
@@ -106,6 +107,9 @@
maxAttrib = slot;
}
delete name;
+
+ while (GLenum error = hooks->gl.glGetError())
+ LOGD("DbgContext::glUseProgram: after glGetError() = 0x%.4X", error);
}
static bool HasNonVBOAttribs(const DbgContext * const ctx)
diff --git a/opengl/libs/GLES2_dbg/src/debugger_message.pb.cpp b/opengl/libs/GLES2_dbg/src/debugger_message.pb.cpp
index 4083c44..1f404c2 100644
--- a/opengl/libs/GLES2_dbg/src/debugger_message.pb.cpp
+++ b/opengl/libs/GLES2_dbg/src/debugger_message.pb.cpp
@@ -229,7 +229,6 @@
case 188:
case 189:
case 190:
- case 191:
return true;
default:
return false;
@@ -428,7 +427,6 @@
const Message_Function Message::CONTINUE;
const Message_Function Message::SKIP;
const Message_Function Message::SETPROP;
-const Message_Function Message::CAPTURE;
const Message_Function Message::Function_MIN;
const Message_Function Message::Function_MAX;
const int Message::Function_ARRAYSIZE;
diff --git a/opengl/libs/GLES2_dbg/src/debugger_message.pb.h b/opengl/libs/GLES2_dbg/src/debugger_message.pb.h
index 3f2b842..59e7bab 100644
--- a/opengl/libs/GLES2_dbg/src/debugger_message.pb.h
+++ b/opengl/libs/GLES2_dbg/src/debugger_message.pb.h
@@ -226,12 +226,11 @@
Message_Function_NEG = 187,
Message_Function_CONTINUE = 188,
Message_Function_SKIP = 189,
- Message_Function_SETPROP = 190,
- Message_Function_CAPTURE = 191
+ Message_Function_SETPROP = 190
};
bool Message_Function_IsValid(int value);
const Message_Function Message_Function_Function_MIN = Message_Function_glActiveTexture;
-const Message_Function Message_Function_Function_MAX = Message_Function_CAPTURE;
+const Message_Function Message_Function_Function_MAX = Message_Function_SETPROP;
const int Message_Function_Function_ARRAYSIZE = Message_Function_Function_MAX + 1;
enum Message_Type {
@@ -488,7 +487,6 @@
static const Function CONTINUE = Message_Function_CONTINUE;
static const Function SKIP = Message_Function_SKIP;
static const Function SETPROP = Message_Function_SETPROP;
- static const Function CAPTURE = Message_Function_CAPTURE;
static inline bool Function_IsValid(int value) {
return Message_Function_IsValid(value);
}
diff --git a/opengl/libs/GLES2_dbg/src/egl.cpp b/opengl/libs/GLES2_dbg/src/egl.cpp
index b3979a3..27c7f7e 100644
--- a/opengl/libs/GLES2_dbg/src/egl.cpp
+++ b/opengl/libs/GLES2_dbg/src/egl.cpp
@@ -19,7 +19,7 @@
EGLBoolean Debug_eglSwapBuffers(EGLDisplay dpy, EGLSurface draw)
{
glesv2debugger::Message msg;
- const bool expectResponse = true;
+ const bool expectResponse = false;
struct : public FunctionCall {
EGLDisplay dpy;
EGLSurface draw;
diff --git a/opengl/libs/GLES2_dbg/src/header.h b/opengl/libs/GLES2_dbg/src/header.h
index cbd448a..b79cc0f 100644
--- a/opengl/libs/GLES2_dbg/src/header.h
+++ b/opengl/libs/GLES2_dbg/src/header.h
@@ -113,7 +113,7 @@
virtual ~FunctionCall() {}
};
-// move these into DbgContext
+// move these into DbgContext as static
extern bool capture;
extern int timeMode; // SYSTEM_TIME_
@@ -121,8 +121,10 @@
unsigned GetBytesPerPixel(const GLenum format, const GLenum type);
+// every Debug_gl* function calls this to send message to client and possibly receive commands
int * MessageLoop(FunctionCall & functionCall, glesv2debugger::Message & msg,
const bool expectResponse, const glesv2debugger::Message_Function function);
+
void Receive(glesv2debugger::Message & cmd);
float Send(const glesv2debugger::Message & msg, glesv2debugger::Message & cmd);
void SetProp(const glesv2debugger::Message & cmd);
diff --git a/opengl/libs/GLES2_dbg/src/server.cpp b/opengl/libs/GLES2_dbg/src/server.cpp
index 820e9de..03c3dae 100644
--- a/opengl/libs/GLES2_dbg/src/server.cpp
+++ b/opengl/libs/GLES2_dbg/src/server.cpp
@@ -38,7 +38,7 @@
exit(1);
}
-void StartDebugServer()
+void StartDebugServer(unsigned short port)
{
LOGD("GLESv2_dbg: StartDebugServer");
if (serverSock >= 0)
@@ -53,8 +53,8 @@
}
/* Construct the server sockaddr_in structure */
server.sin_family = AF_INET; /* Internet/IP */
- server.sin_addr.s_addr = htonl(INADDR_ANY); /* Incoming addr */
- server.sin_port = htons(5039); /* server port */
+ server.sin_addr.s_addr = htonl(INADDR_LOOPBACK); /* Incoming addr */
+ server.sin_port = htons(port); /* server port */
/* Bind the server socket */
socklen_t sizeofSockaddr_in = sizeof(sockaddr_in);
@@ -79,13 +79,6 @@
LOGD("Client connected: %s\n", inet_ntoa(client.sin_addr));
// fcntl(clientSock, F_SETFL, O_NONBLOCK);
-
- glesv2debugger::Message msg, cmd;
- msg.set_context_id(0);
- msg.set_function(glesv2debugger::Message_Function_ACK);
- msg.set_type(glesv2debugger::Message_Type_Response);
- msg.set_expect_response(false);
- Send(msg, cmd);
}
void StopDebugServer()
@@ -130,6 +123,27 @@
cmd.ParseFromArray(buffer, len);
}
+bool TryReceive(glesv2debugger::Message & cmd)
+{
+ fd_set readSet;
+ FD_ZERO(&readSet);
+ FD_SET(clientSock, &readSet);
+ timeval timeout;
+ timeout.tv_sec = timeout.tv_usec = 0;
+
+ int rc = select(clientSock + 1, &readSet, NULL, NULL, &timeout);
+ if (rc < 0)
+ Die("failed to select clientSock");
+
+ bool received = false;
+ if (FD_ISSET(clientSock, &readSet)) {
+ LOGD("TryReceive: avaiable for read");
+ Receive(cmd);
+ return true;
+ }
+ return false;
+}
+
float Send(const glesv2debugger::Message & msg, glesv2debugger::Message & cmd)
{
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
@@ -155,12 +169,18 @@
Die("Failed to send message");
}
+ // try to receive commands even though not expecting response,
+ // since client can send SETPROP commands anytime
if (!msg.expect_response()) {
- pthread_mutex_unlock(&mutex);
- return t;
- }
-
- Receive(cmd);
+ if (TryReceive(cmd)) {
+ LOGD("Send: TryReceived");
+ if (glesv2debugger::Message_Function_SETPROP == cmd.function())
+ LOGD("Send: received SETPROP");
+ else
+ LOGD("Send: received something else");
+ }
+ } else
+ Receive(cmd);
//LOGD("Message sent tid=%lu len=%d", pthread_self(), str.length());
pthread_mutex_unlock(&mutex);
@@ -193,24 +213,26 @@
msg.set_type(glesv2debugger::Message_Type_BeforeCall);
msg.set_expect_response(expectResponse);
msg.set_function(function);
- Send(msg, cmd);
if (!expectResponse)
cmd.set_function(glesv2debugger::Message_Function_CONTINUE);
+ Send(msg, cmd);
while (true) {
msg.Clear();
nsecs_t c0 = systemTime(timeMode);
switch (cmd.function()) {
case glesv2debugger::Message_Function_CONTINUE:
ret = functionCall(&dbg->hooks->gl, msg);
+ while (GLenum error = dbg->hooks->gl.glGetError())
+ LOGD("Function=%u glGetError() = 0x%.4X", function, error);
if (!msg.has_time()) // some has output data copy, so time inside call
msg.set_time((systemTime(timeMode) - c0) * 1e-6f);
msg.set_context_id(reinterpret_cast<int>(dbg));
msg.set_function(function);
msg.set_type(glesv2debugger::Message_Type_AfterCall);
msg.set_expect_response(expectResponse);
- Send(msg, cmd);
if (!expectResponse)
cmd.set_function(glesv2debugger::Message_Function_SKIP);
+ Send(msg, cmd);
break;
case glesv2debugger::Message_Function_SKIP:
return const_cast<int *>(ret);
diff --git a/opengl/libs/GLES2_dbg/src/vertex.cpp b/opengl/libs/GLES2_dbg/src/vertex.cpp
index 52ce907..a73967f 100644
--- a/opengl/libs/GLES2_dbg/src/vertex.cpp
+++ b/opengl/libs/GLES2_dbg/src/vertex.cpp
@@ -41,10 +41,10 @@
msg.set_arg6(reinterpret_cast<int>(pixels));
//void * data = NULL;
//unsigned encodedSize = 0;
- Send(msg, cmd);
- float t = 0;
if (!expectResponse)
cmd.set_function(glesv2debugger::Message_Function_CONTINUE);
+ Send(msg, cmd);
+ float t = 0;
while (true) {
msg.Clear();
nsecs_t c0 = systemTime(timeMode);
@@ -61,6 +61,8 @@
//msg.set_data(data, encodedSize);
//free(data);
c0 = systemTime(timeMode);
+ if (!expectResponse)
+ cmd.set_function(glesv2debugger::Message_Function_SKIP);
t = Send(msg, cmd);
msg.set_time((systemTime(timeMode) - c0) * 1e-6f);
msg.set_clock(t);
@@ -69,11 +71,13 @@
msg.set_expect_response(false);
msg.set_type(glesv2debugger::Message_Type_AfterCall);
//Send(msg, cmd);
- if (!expectResponse)
- cmd.set_function(glesv2debugger::Message_Function_SKIP);
break;
case glesv2debugger::Message_Function_SKIP:
return;
+ case glesv2debugger::Message_Function_SETPROP:
+ SetProp(cmd);
+ Receive(cmd);
+ break;
default:
assert(0); //GenerateCall(msg, cmd);
break;
@@ -104,9 +108,9 @@
void * pixels = NULL;
GLint readFormat = 0, readType = 0;
int viewport[4] = {};
- Send(msg, cmd);
if (!expectResponse)
cmd.set_function(glesv2debugger::Message_Function_CONTINUE);
+ Send(msg, cmd);
while (true) {
msg.Clear();
nsecs_t c0 = systemTime(timeMode);
@@ -118,25 +122,26 @@
msg.set_function(glesv2debugger::Message_Function_glDrawArrays);
msg.set_type(glesv2debugger::Message_Type_AfterCall);
msg.set_expect_response(expectResponse);
- Send(msg, cmd);
- if (capture)
- cmd.set_function(glesv2debugger::Message_Function_CAPTURE);
- else if (!expectResponse)
+ if (!expectResponse)
cmd.set_function(glesv2debugger::Message_Function_SKIP);
+ Send(msg, cmd);
+ if (capture) {
+ dbg->hooks->gl.glGetIntegerv(GL_VIEWPORT, viewport);
+ dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readFormat);
+ dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &readType);
+ LOGD("glDrawArrays CAPTURE: x=%d y=%d width=%d height=%d format=0x%.4X type=0x%.4X",
+ viewport[0], viewport[1], viewport[2], viewport[3], readFormat, readType);
+ pixels = malloc(viewport[2] * viewport[3] * 4);
+ Debug_glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3],
+ readFormat, readType, pixels);
+ free(pixels);
+ }
break;
case glesv2debugger::Message_Function_SKIP:
return;
- case glesv2debugger::Message_Function_CAPTURE:
- dbg->hooks->gl.glGetIntegerv(GL_VIEWPORT, viewport);
- dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readFormat);
- dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &readType);
- LOGD("glDrawArrays CAPTURE: x=%d y=%d width=%d height=%d format=0x%.4X type=0x%.4X",
- viewport[0], viewport[1], viewport[2], viewport[3], readFormat, readType);
- pixels = malloc(viewport[2] * viewport[3] * 4);
- Debug_glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3],
- readFormat, readType, pixels);
- free(pixels);
- cmd.set_function(glesv2debugger::Message_Function_SKIP);
+ case glesv2debugger::Message_Function_SETPROP:
+ SetProp(cmd);
+ Receive(cmd);
break;
default:
assert(0); //GenerateCall(msg, cmd);
@@ -189,9 +194,9 @@
void * pixels = NULL;
GLint readFormat = 0, readType = 0;
int viewport[4] = {};
- Send(msg, cmd);
if (!expectResponse)
cmd.set_function(glesv2debugger::Message_Function_CONTINUE);
+ Send(msg, cmd);
while (true) {
msg.Clear();
nsecs_t c0 = systemTime(timeMode);
@@ -203,25 +208,26 @@
msg.set_function(glesv2debugger::Message_Function_glDrawElements);
msg.set_type(glesv2debugger::Message_Type_AfterCall);
msg.set_expect_response(expectResponse);
- Send(msg, cmd);
- if (capture)
- cmd.set_function(glesv2debugger::Message_Function_CAPTURE);
- else if (!expectResponse)
+ if (!expectResponse)
cmd.set_function(glesv2debugger::Message_Function_SKIP);
+ Send(msg, cmd);
+ if (capture) {
+ dbg->hooks->gl.glGetIntegerv(GL_VIEWPORT, viewport);
+ dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readFormat);
+ dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &readType);
+ LOGD("glDrawArrays CAPTURE: x=%d y=%d width=%d height=%d format=0x%.4X type=0x%.4X",
+ viewport[0], viewport[1], viewport[2], viewport[3], readFormat, readType);
+ pixels = malloc(viewport[2] * viewport[3] * 4);
+ Debug_glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3],
+ readFormat, readType, pixels);
+ free(pixels);
+ }
break;
case glesv2debugger::Message_Function_SKIP:
return;
- case glesv2debugger::Message_Function_CAPTURE:
- dbg->hooks->gl.glGetIntegerv(GL_VIEWPORT, viewport);
- dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readFormat);
- dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &readType);
- LOGD("glDrawArrays CAPTURE: x=%d y=%d width=%d height=%d format=0x%.4X type=0x%.4X",
- viewport[0], viewport[1], viewport[2], viewport[3], readFormat, readType);
- pixels = malloc(viewport[2] * viewport[3] * 4);
- Debug_glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3],
- readFormat, readType, pixels);
- free(pixels);
- cmd.set_function(glesv2debugger::Message_Function_SKIP);
+ case glesv2debugger::Message_Function_SETPROP:
+ SetProp(cmd);
+ Receive(cmd);
break;
default:
assert(0); //GenerateCall(msg, cmd);
diff --git a/opengl/libs/glesv2dbg.h b/opengl/libs/glesv2dbg.h
index b988eb78..8029dce 100644
--- a/opengl/libs/glesv2dbg.h
+++ b/opengl/libs/glesv2dbg.h
@@ -24,7 +24,7 @@
DbgContext * CreateDbgContext(const unsigned version, const gl_hooks_t * const hooks);
void DestroyDbgContext(DbgContext * const dbg);
- void StartDebugServer(); // create and bind socket if haven't already
+ void StartDebugServer(unsigned short port); // create and bind socket if haven't already
void StopDebugServer(); // close socket if open
}; // namespace android
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 7a69097..c4027e0 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -811,7 +811,7 @@
{ // scope for strong mUserClient reference
sp<UserClient> userClient(mUserClient.promote());
- if (mUserClient != 0 && mControlBlock != 0) {
+ if (userClient != 0 && mControlBlock != 0) {
mControlBlock->setStatus(NO_INIT);
}
}
@@ -860,11 +860,13 @@
Mutex::Autolock _l(mLock);
if (size < mNumBuffers) {
- // Move the active texture into slot 0
- BufferData activeBufferData = mBufferData[mActiveBufferIndex];
- mBufferData[mActiveBufferIndex] = mBufferData[0];
- mBufferData[0] = activeBufferData;
- mActiveBufferIndex = 0;
+ // If there is an active texture, move it into slot 0 if needed
+ if (mActiveBufferIndex > 0) {
+ BufferData activeBufferData = mBufferData[mActiveBufferIndex];
+ mBufferData[mActiveBufferIndex] = mBufferData[0];
+ mBufferData[0] = activeBufferData;
+ mActiveBufferIndex = 0;
+ }
// Free the buffers that are no longer needed.
for (size_t i = size; i < mNumBuffers; i++) {
diff --git a/services/surfaceflinger/tests/resize/resize.cpp b/services/surfaceflinger/tests/resize/resize.cpp
index 99f4b4f..0ccca77 100644
--- a/services/surfaceflinger/tests/resize/resize.cpp
+++ b/services/surfaceflinger/tests/resize/resize.cpp
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2010 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 <cutils/memory.h>
#include <utils/Log.h>
diff --git a/services/surfaceflinger/tests/surface/surface.cpp b/services/surfaceflinger/tests/surface/surface.cpp
index 194fbb6..67ecf7e 100644
--- a/services/surfaceflinger/tests/surface/surface.cpp
+++ b/services/surfaceflinger/tests/surface/surface.cpp
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2010 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 <cutils/memory.h>
#include <utils/Log.h>