diff options
46 files changed, 2522 insertions, 2964 deletions
diff --git a/cmds/keystore/keystore.cpp b/cmds/keystore/keystore.cpp index 05f77e5326..2c9cb35b7b 100644 --- a/cmds/keystore/keystore.cpp +++ b/cmds/keystore/keystore.cpp @@ -581,7 +581,7 @@ static ResponseCode get(KeyStore* keyStore, int sock, uid_t uid, Value* keyName, static ResponseCode insert(KeyStore* keyStore, int sock, uid_t uid, Value* keyName, Value* val) { char filename[NAME_MAX]; encode_key(filename, uid, keyName); - Blob keyBlob(val->value, val->length, 0, NULL); + Blob keyBlob(val->value, val->length, NULL, 0); return keyStore->put(filename, &keyBlob); } diff --git a/cmds/surfaceflinger/main_surfaceflinger.cpp b/cmds/surfaceflinger/main_surfaceflinger.cpp index 78b1007f5e..6dbcf5c2fe 100644 --- a/cmds/surfaceflinger/main_surfaceflinger.cpp +++ b/cmds/surfaceflinger/main_surfaceflinger.cpp @@ -20,6 +20,6 @@ using namespace android; int main(int argc, char** argv) { - SurfaceFlinger::publishAndJoinThreadPool(); + SurfaceFlinger::publishAndJoinThreadPool(true); return 0; } diff --git a/include/binder/BinderService.h b/include/binder/BinderService.h index 2316fef788..ca594d3635 100644 --- a/include/binder/BinderService.h +++ b/include/binder/BinderService.h @@ -34,15 +34,15 @@ template<typename SERVICE> class BinderService { public: - static status_t publish() { + static status_t publish(bool allowIsolated = false) { sp<IServiceManager> sm(defaultServiceManager()); - return sm->addService(String16(SERVICE::getServiceName()), new SERVICE()); + return sm->addService(String16(SERVICE::getServiceName()), new SERVICE(), allowIsolated); } - static void publishAndJoinThreadPool() { + static void publishAndJoinThreadPool(bool allowIsolated = false) { sp<ProcessState> proc(ProcessState::self()); sp<IServiceManager> sm(defaultServiceManager()); - sm->addService(String16(SERVICE::getServiceName()), new SERVICE()); + sm->addService(String16(SERVICE::getServiceName()), new SERVICE(), allowIsolated); ProcessState::self()->startThreadPool(); IPCThreadState::self()->joinThreadPool(); } diff --git a/include/binder/IPCThreadState.h b/include/binder/IPCThreadState.h index 3378d97255..691ba2fa1d 100644 --- a/include/binder/IPCThreadState.h +++ b/include/binder/IPCThreadState.h @@ -41,6 +41,7 @@ public: int getCallingPid(); int getCallingUid(); + int getOrigCallingUid(); void setStrictModePolicy(int32_t policy); int32_t getStrictModePolicy() const; @@ -116,6 +117,7 @@ private: status_t mLastError; pid_t mCallingPid; uid_t mCallingUid; + uid_t mOrigCallingUid; int32_t mStrictModePolicy; int32_t mLastTransactionBinderFlags; }; diff --git a/include/binder/IServiceManager.h b/include/binder/IServiceManager.h index 24e9e992cc..2c297d64fb 100644 --- a/include/binder/IServiceManager.h +++ b/include/binder/IServiceManager.h @@ -47,7 +47,8 @@ public: * Register a service. */ virtual status_t addService( const String16& name, - const sp<IBinder>& service) = 0; + const sp<IBinder>& service, + bool allowIsolated = false) = 0; /** * Return list of all existing services. diff --git a/include/gui/BufferQueue.h b/include/gui/BufferQueue.h new file mode 100644 index 0000000000..991a1817ac --- /dev/null +++ b/include/gui/BufferQueue.h @@ -0,0 +1,343 @@ +/* + * Copyright (C) 2012 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_GUI_BUFFERQUEUE_H +#define ANDROID_GUI_BUFFERQUEUE_H + +#include <EGL/egl.h> + +#include <gui/ISurfaceTexture.h> + +#include <surfaceflinger/IGraphicBufferAlloc.h> +#include <ui/GraphicBuffer.h> + +#include <utils/String8.h> +#include <utils/Vector.h> +#include <utils/threads.h> + +namespace android { +// ---------------------------------------------------------------------------- + +class BufferQueue : public BnSurfaceTexture { +public: + enum { MIN_UNDEQUEUED_BUFFERS = 2 }; + enum { + MIN_ASYNC_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS + 1, + MIN_SYNC_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS + }; + enum { NUM_BUFFER_SLOTS = 32 }; + enum { NO_CONNECTED_API = 0 }; + + struct FrameAvailableListener : public virtual RefBase { + // onFrameAvailable() is called from queueBuffer() each time an + // additional frame becomes available for consumption. This means that + // frames that are queued while in asynchronous mode only trigger the + // callback if no previous frames are pending. Frames queued while in + // synchronous mode always trigger the callback. + // + // This is called without any lock held and can be called concurrently + // by multiple threads. + virtual void onFrameAvailable() = 0; + }; + + // BufferQueue manages a pool of gralloc memory slots to be used + // by producers and consumers. + // allowSynchronousMode specifies whether or not synchronous mode can be + // enabled. + BufferQueue(bool allowSynchronousMode = true); + virtual ~BufferQueue(); + + // setBufferCount updates the number of available buffer slots. After + // calling this all buffer slots are both unallocated and owned by the + // BufferQueue object (i.e. they are not owned by the client). + virtual status_t setBufferCount(int bufferCount); + + virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf); + + // dequeueBuffer gets the next buffer slot index for the client to use. If a + // buffer slot is available then that slot index is written to the location + // pointed to by the buf argument and a status of OK is returned. If no + // slot is available then a status of -EBUSY is returned and buf is + // unmodified. + // The width and height parameters must be no greater than the minimum of + // GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv). + // An error due to invalid dimensions might not be reported until + // updateTexImage() is called. + virtual status_t dequeueBuffer(int *buf, uint32_t width, uint32_t height, + uint32_t format, uint32_t usage); + + // queueBuffer returns a filled buffer to the BufferQueue. 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, + uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform); + virtual void cancelBuffer(int buf); + virtual status_t setCrop(const Rect& reg); + virtual status_t setTransform(uint32_t transform); + virtual status_t setScalingMode(int mode); + + // setSynchronousMode set whether dequeueBuffer is synchronous or + // asynchronous. In synchronous mode, dequeueBuffer blocks until + // a buffer is available, the currently bound buffer can be dequeued and + // queued buffers will be retired in order. + // The default mode is asynchronous. + virtual status_t setSynchronousMode(bool enabled); + + // connect attempts to connect a producer client API to the BufferQueue. + // This must be called before any other ISurfaceTexture methods are called + // except for getAllocator. + // + // This method will fail if the connect was previously called on the + // BufferQueue and no corresponding disconnect call was made. + virtual status_t connect(int api, + uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform); + + // disconnect attempts to disconnect a producer client API from the + // BufferQueue. Calling this method will cause any subsequent calls to other + // ISurfaceTexture methods to fail except for getAllocator and connect. + // Successfully calling connect after this will allow the other methods to + // succeed again. + // + // This method will fail if the the BufferQueue is not currently + // connected to the specified client API. + virtual status_t disconnect(int api); + +protected: + + // freeBufferLocked frees the resources (both GraphicBuffer and EGLImage) + // for the given slot. + void freeBufferLocked(int index); + + // freeAllBuffersLocked frees the resources (both GraphicBuffer and + // EGLImage) for all slots. + void freeAllBuffersLocked(); + + // freeAllBuffersExceptHeadLocked frees the resources (both GraphicBuffer + // and EGLImage) for all slots except the head of mQueue + void freeAllBuffersExceptHeadLocked(); + + // drainQueueLocked drains the buffer queue if we're in synchronous mode + // returns immediately otherwise. It returns NO_INIT if the BufferQueue + // became abandoned or disconnected during this call. + status_t drainQueueLocked(); + + // drainQueueAndFreeBuffersLocked drains the buffer queue if we're in + // synchronous mode and free all buffers. In asynchronous mode, all buffers + // are freed except the current buffer. + status_t drainQueueAndFreeBuffersLocked(); + + status_t setBufferCountServerLocked(int bufferCount); + + enum { INVALID_BUFFER_SLOT = -1 }; + + struct BufferSlot { + + BufferSlot() + : mEglImage(EGL_NO_IMAGE_KHR), + mEglDisplay(EGL_NO_DISPLAY), + mBufferState(BufferSlot::FREE), + mRequestBufferCalled(false), + mTransform(0), + mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), + mTimestamp(0), + mFrameNumber(0), + mFence(EGL_NO_SYNC_KHR) { + mCrop.makeInvalid(); + } + + // mGraphicBuffer points to the buffer allocated for this slot or is NULL + // if no buffer has been allocated. + sp<GraphicBuffer> mGraphicBuffer; + + // mEglImage is the EGLImage created from mGraphicBuffer. + EGLImageKHR mEglImage; + + // mEglDisplay is the EGLDisplay used to create mEglImage. + EGLDisplay mEglDisplay; + + // BufferState represents the different states in which a buffer slot + // can be. + enum BufferState { + // FREE indicates that the buffer is not currently being used and + // will not be used in the future until it gets dequeued and + // subsequently queued by the client. + FREE = 0, + + // DEQUEUED indicates that the buffer has been dequeued by the + // client, but has not yet been queued or canceled. The buffer is + // considered 'owned' by the client, and the server should not use + // it for anything. + // + // Note that when in synchronous-mode (mSynchronousMode == true), + // the buffer that's currently attached to the texture may be + // dequeued by the client. That means that the current buffer can + // be in either the DEQUEUED or QUEUED state. In asynchronous mode, + // however, the current buffer is always in the QUEUED state. + DEQUEUED = 1, + + // QUEUED indicates that the buffer has been queued by the client, + // and has not since been made available for the client to dequeue. + // Attaching the buffer to the texture does NOT transition the + // buffer away from the QUEUED state. However, in Synchronous mode + // the current buffer may be dequeued by the client under some + // circumstances. See the note about the current buffer in the + // documentation for DEQUEUED. + QUEUED = 2, + }; + + // mBufferState is the current state of this buffer slot. + BufferState mBufferState; + + // mRequestBufferCalled is used for validating that the client did + // call requestBuffer() when told to do so. Technically this is not + // needed but useful for debugging and catching client bugs. + bool mRequestBufferCalled; + + // mCrop is the current crop rectangle for this buffer slot. This gets + // set to mNextCrop each time queueBuffer gets called for this buffer. + Rect mCrop; + + // mTransform is the current transform flags for this buffer slot. This + // gets set to mNextTransform each time queueBuffer gets called for this + // slot. + uint32_t mTransform; + + // mScalingMode is the current scaling mode for this buffer slot. This + // gets set to mNextScalingMode each time queueBuffer gets called for + // this slot. + uint32_t mScalingMode; + + // mTimestamp is the current timestamp for this buffer slot. This gets + // to set by queueBuffer each time this slot is queued. + int64_t mTimestamp; + + // mFrameNumber is the number of the queued frame for this slot. + uint64_t mFrameNumber; + + // mFence is the EGL sync object that must signal before the buffer + // associated with this buffer slot may be dequeued. It is initialized + // to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based + // on a compile-time option) set to a new sync object in updateTexImage. + EGLSyncKHR mFence; + }; + + // mSlots is the array of buffer slots that must be mirrored on the client + // side. This allows buffer ownership to be transferred between the client + // and server without sending a GraphicBuffer over binder. The entire array + // is initialized to NULL at construction time, and buffers are allocated + // for a slot when requestBuffer is called with that slot's index. + BufferSlot mSlots[NUM_BUFFER_SLOTS]; + + + // mDefaultWidth holds the default width of allocated buffers. It is used + // in requestBuffers() if a width and height of zero is specified. + uint32_t mDefaultWidth; + + // mDefaultHeight holds the default height of allocated buffers. It is used + // in requestBuffers() if a width and height of zero is specified. + uint32_t mDefaultHeight; + + // mPixelFormat holds the pixel format of allocated buffers. It is used + // in requestBuffers() if a format of zero is specified. + uint32_t mPixelFormat; + + // mBufferCount is the number of buffer slots that the client and server + // must maintain. It defaults to MIN_ASYNC_BUFFER_SLOTS and can be changed + // by calling setBufferCount or setBufferCountServer + int mBufferCount; + + // mClientBufferCount is the number of buffer slots requested by the client. + // The default is zero, which means the client doesn't care how many buffers + // there is. + int mClientBufferCount; + + // mServerBufferCount buffer count requested by the server-side + int mServerBufferCount; + + // mCurrentTexture is the buffer slot index of the buffer that is currently + // bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT, + // indicating that no buffer slot is currently bound to the texture. Note, + // however, that a value of INVALID_BUFFER_SLOT does not necessarily mean + // that no buffer is bound to the texture. A call to setBufferCount will + // reset mCurrentTexture to INVALID_BUFFER_SLOT. + int mCurrentTexture; + + // mNextCrop is the crop rectangle that will be used for the next buffer + // that gets queued. It is set by calling setCrop. + Rect mNextCrop; + + // mNextTransform is the transform identifier that will be used for the next + // buffer that gets queued. It is set by calling setTransform. + uint32_t mNextTransform; + + // mNextScalingMode is the scaling mode that will be used for the next + // buffers that get queued. It is set by calling setScalingMode. + int mNextScalingMode; + + // mGraphicBufferAlloc is the connection to SurfaceFlinger that is used to + // allocate new GraphicBuffer objects. + sp<IGraphicBufferAlloc> mGraphicBufferAlloc; + + // mFrameAvailableListener is the listener object that will be called when a + // new frame becomes available. If it is not NULL it will be called from + // queueBuffer. + sp<FrameAvailableListener> mFrameAvailableListener; + + // mSynchronousMode whether we're in synchronous mode or not + bool mSynchronousMode; + + // mAllowSynchronousMode whether we allow synchronous mode or not + const bool mAllowSynchronousMode; + + // mConnectedApi indicates the API that is currently connected to this + // BufferQueue. It defaults to NO_CONNECTED_API (= 0), and gets updated + // by the connect and disconnect methods. + int mConnectedApi; + + // mDequeueCondition condition used for dequeueBuffer in synchronous mode + mutable Condition mDequeueCondition; + + // mQueue is a FIFO of queued buffers used in synchronous mode + typedef Vector<int> Fifo; + Fifo mQueue; + + // mAbandoned indicates that the BufferQueue will no longer be used to + // consume images buffers pushed to it using the ISurfaceTexture interface. + // It is initialized to false, and set to true in the abandon method. A + // BufferQueue that has been abandoned will return the NO_INIT error from + // all ISurfaceTexture methods capable of returning an error. + bool mAbandoned; + + // mName is a string used to identify the BufferQueue in log messages. + // It is set by the setName method. + String8 mName; + + // mMutex is the mutex used to prevent concurrent access to the member + // variables of BufferQueue objects. It must be locked whenever the + // member variables are accessed. + mutable Mutex mMutex; + + // mFrameCounter is the free running counter, incremented for every buffer queued + // with the surface Texture. + uint64_t mFrameCounter; +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_GUI_BUFFERQUEUE_H diff --git a/include/gui/DisplayEventReceiver.h b/include/gui/DisplayEventReceiver.h index a28137785e..7bca8d6ea8 100644 --- a/include/gui/DisplayEventReceiver.h +++ b/include/gui/DisplayEventReceiver.h @@ -95,6 +95,8 @@ public: * should be destroyed and getEvents() shouldn't be called again. */ ssize_t getEvents(Event* events, size_t count); + static ssize_t getEvents(const sp<BitTube>& dataChannel, + Event* events, size_t count); /* * setVsyncRate() sets the Event::VSync delivery rate. A value of diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h index a8c76725e7..4318f0fa48 100644 --- a/include/gui/SurfaceTexture.h +++ b/include/gui/SurfaceTexture.h @@ -23,6 +23,7 @@ #include <GLES2/gl2ext.h> #include <gui/ISurfaceTexture.h> +#include <gui/BufferQueue.h> #include <ui/GraphicBuffer.h> @@ -35,30 +36,11 @@ namespace android { // ---------------------------------------------------------------------------- -class IGraphicBufferAlloc; + class String8; -class SurfaceTexture : public BnSurfaceTexture { +class SurfaceTexture : public BufferQueue { public: - enum { MIN_UNDEQUEUED_BUFFERS = 2 }; - enum { - MIN_ASYNC_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS + 1, - MIN_SYNC_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS - }; - enum { NUM_BUFFER_SLOTS = 32 }; - enum { NO_CONNECTED_API = 0 }; - - struct FrameAvailableListener : public virtual RefBase { - // onFrameAvailable() is called from queueBuffer() each time an - // additional frame becomes available for consumption. This means that - // frames that are queued while in asynchronous mode only trigger the - // callback if no previous frames are pending. Frames queued while in - // synchronous mode always trigger the callback. - // - // This is called without any lock held and can be called concurrently - // by multiple threads. - virtual void onFrameAvailable() = 0; - }; // SurfaceTexture constructs a new SurfaceTexture object. tex indicates the // name of the OpenGL ES texture to which images are to be streamed. This @@ -73,65 +55,8 @@ public: virtual ~SurfaceTexture(); - // setBufferCount updates the number of available buffer slots. After - // calling this all buffer slots are both unallocated and owned by the - // SurfaceTexture object (i.e. they are not owned by the client). - virtual status_t setBufferCount(int bufferCount); - - virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf); - - // dequeueBuffer gets the next buffer slot index for the client to use. If a - // buffer slot is available then that slot index is written to the location - // pointed to by the buf argument and a status of OK is returned. If no - // slot is available then a status of -EBUSY is returned and buf is - // unmodified. - // The width and height parameters must be no greater than the minimum of - // GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv). - // An error due to invalid dimensions might not be reported until - // updateTexImage() is called. - virtual status_t dequeueBuffer(int *buf, uint32_t width, uint32_t height, - uint32_t format, uint32_t usage); - - // 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, - uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform); - virtual void cancelBuffer(int buf); - virtual status_t setCrop(const Rect& reg); - virtual status_t setTransform(uint32_t transform); - virtual status_t setScalingMode(int mode); - virtual int query(int what, int* value); - // setSynchronousMode set whether dequeueBuffer is synchronous or - // asynchronous. In synchronous mode, dequeueBuffer blocks until - // a buffer is available, the currently bound buffer can be dequeued and - // queued buffers will be retired in order. - // The default mode is asynchronous. - virtual status_t setSynchronousMode(bool enabled); - - // connect attempts to connect a client API to the SurfaceTexture. This - // must be called before any other ISurfaceTexture methods are called except - // for getAllocator. - // - // This method will fail if the connect was previously called on the - // SurfaceTexture and no corresponding disconnect call was made. - virtual status_t connect(int api, - uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform); - - // disconnect attempts to disconnect a client API from the SurfaceTexture. - // Calling this method will cause any subsequent calls to other - // ISurfaceTexture methods to fail except for getAllocator and connect. - // Successfully calling connect after this will allow the other methods to - // succeed again. - // - // This method will fail if the the SurfaceTexture is not currently - // connected to the specified client API. - virtual status_t disconnect(int api); - // updateTexImage sets the image contents of the target texture to that of // the most recently queued buffer. // @@ -233,28 +158,6 @@ public: protected: - // freeBufferLocked frees the resources (both GraphicBuffer and EGLImage) - // for the given slot. - void freeBufferLocked(int index); - - // freeAllBuffersLocked frees the resources (both GraphicBuffer and - // EGLImage) for all slots. - void freeAllBuffersLocked(); - - // freeAllBuffersExceptHeadLocked frees the resources (both GraphicBuffer - // and EGLImage) for all slots except the head of mQueue - void freeAllBuffersExceptHeadLocked(); - - // drainQueueLocked drains the buffer queue if we're in synchronous mode - // returns immediately otherwise. return NO_INIT if SurfaceTexture - // became abandoned or disconnected during this call. - status_t drainQueueLocked(); - - // drainQueueAndFreeBuffersLocked drains the buffer queue if we're in - // synchronous mode and free all buffers. In asynchronous mode, all buffers - // are freed except the current buffer. - status_t drainQueueAndFreeBuffersLocked(); - static bool isExternalFormat(uint32_t format); private: @@ -263,146 +166,11 @@ private: EGLImageKHR createImage(EGLDisplay dpy, const sp<GraphicBuffer>& graphicBuffer); - status_t setBufferCountServerLocked(int bufferCount); - // computeCurrentTransformMatrix computes the transform matrix for the // current texture. It uses mCurrentTransform and the current GraphicBuffer // to compute this matrix and stores it in mCurrentTransformMatrix. void computeCurrentTransformMatrix(); - enum { INVALID_BUFFER_SLOT = -1 }; - - struct BufferSlot { - - BufferSlot() - : mEglImage(EGL_NO_IMAGE_KHR), - mEglDisplay(EGL_NO_DISPLAY), - mBufferState(BufferSlot::FREE), - mRequestBufferCalled(false), - mTransform(0), - mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), - mTimestamp(0), - mFrameNumber(0), - mFence(EGL_NO_SYNC_KHR) { - mCrop.makeInvalid(); - } - - // mGraphicBuffer points to the buffer allocated for this slot or is NULL - // if no buffer has been allocated. - sp<GraphicBuffer> mGraphicBuffer; - - // mEglImage is the EGLImage created from mGraphicBuffer. - EGLImageKHR mEglImage; - - // mEglDisplay is the EGLDisplay used to create mEglImage. - EGLDisplay mEglDisplay; - - // BufferState represents the different states in which a buffer slot - // can be. - enum BufferState { - // FREE indicates that the buffer is not currently being used and - // will not be used in the future until it gets dequeued and - // subsequently queued by the client. - FREE = 0, - - // DEQUEUED indicates that the buffer has been dequeued by the - // client, but has not yet been queued or canceled. The buffer is - // considered 'owned' by the client, and the server should not use - // it for anything. - // - // Note that when in synchronous-mode (mSynchronousMode == true), - // the buffer that's currently attached to the texture may be - // dequeued by the client. That means that the current buffer can - // be in either the DEQUEUED or QUEUED state. In asynchronous mode, - // however, the current buffer is always in the QUEUED state. - DEQUEUED = 1, - - // QUEUED indicates that the buffer has been queued by the client, - // and has not since been made available for the client to dequeue. - // Attaching the buffer to the texture does NOT transition the - // buffer away from the QUEUED state. However, in Synchronous mode - // the current buffer may be dequeued by the client under some - // circumstances. See the note about the current buffer in the - // documentation for DEQUEUED. - QUEUED = 2, - }; - - // mBufferState is the current state of this buffer slot. - BufferState mBufferState; - - // mRequestBufferCalled is used for validating that the client did - // call requestBuffer() when told to do so. Technically this is not - // needed but useful for debugging and catching client bugs. - bool mRequestBufferCalled; - - // mCrop is the current crop rectangle for this buffer slot. This gets - // set to mNextCrop each time queueBuffer gets called for this buffer. - Rect mCrop; - - // mTransform is the current transform flags for this buffer slot. This - // gets set to mNextTransform each time queueBuffer gets called for this - // slot. - uint32_t mTransform; - - // mScalingMode is the current scaling mode for this buffer slot. This - // gets set to mNextScalingMode each time queueBuffer gets called for - // this slot. - uint32_t mScalingMode; - - // mTimestamp is the current timestamp for this buffer slot. This gets - // to set by queueBuffer each time this slot is queued. - int64_t mTimestamp; - - // mFrameNumber is the number of the queued frame for this slot. - uint64_t mFrameNumber; - - // mFence is the EGL sync object that must signal before the buffer - // associated with this buffer slot may be dequeued. It is initialized - // to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based - // on a compile-time option) set to a new sync object in updateTexImage. - EGLSyncKHR mFence; - }; - - // mSlots is the array of buffer slots that must be mirrored on the client - // side. This allows buffer ownership to be transferred between the client - // and server without sending a GraphicBuffer over binder. The entire array - // is initialized to NULL at construction time, and buffers are allocated - // for a slot when requestBuffer is called with that slot's index. - BufferSlot mSlots[NUM_BUFFER_SLOTS]; - - // mDefaultWidth holds the default width of allocated buffers. It is used - // in requestBuffers() if a width and height of zero is specified. - uint32_t mDefaultWidth; - - // mDefaultHeight holds the default height of allocated buffers. It is used - // in requestBuffers() if a width and height of zero is specified. - uint32_t mDefaultHeight; - - // mPixelFormat holds the pixel format of allocated buffers. It is used - // in requestBuffers() if a format of zero is specified. - uint32_t mPixelFormat; - - // mBufferCount is the number of buffer slots that the client and server - // must maintain. It defaults to MIN_ASYNC_BUFFER_SLOTS and can be changed - // by calling setBufferCount or setBufferCountServer - int mBufferCount; - - // mClientBufferCount is the number of buffer slots requested by the client. - // The default is zero, which means the client doesn't care how many buffers - // there is. - int mClientBufferCount; - - // mServerBufferCount buffer count requested by the server-side - int mServerBufferCount; - - // mCurrentTexture is the buffer slot index of the buffer that is currently - // bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT, - // indicating that no buffer slot is currently bound to the texture. Note, - // however, that a value of INVALID_BUFFER_SLOT does not necessarily mean - // that no buffer is bound to the texture. A call to setBufferCount will - // reset mCurrentTexture to INVALID_BUFFER_SLOT. - int mCurrentTexture; - // mCurrentTextureBuf is the graphic buffer of the current texture. It's // possible that this buffer is not associated with any buffer slot, so we // must track it separately in order to support the getCurrentBuffer method. @@ -429,72 +197,17 @@ private: // gets set each time updateTexImage is called. int64_t mCurrentTimestamp; - // mNextCrop is the crop rectangle that will be used for the next buffer - // that gets queued. It is set by calling setCrop. - Rect mNextCrop; - - // mNextTransform is the transform identifier that will be used for the next - // buffer that gets queued. It is set by calling setTransform. - uint32_t mNextTransform; - - // mNextScalingMode is the scaling mode that will be used for the next - // buffers that get queued. It is set by calling setScalingMode. - int mNextScalingMode; - // mTexName is the name of the OpenGL texture to which streamed images will // be bound when updateTexImage is called. It is set at construction time // changed with a call to setTexName. const GLuint mTexName; - // mGraphicBufferAlloc is the connection to SurfaceFlinger that is used to - // allocate new GraphicBuffer objects. - sp<IGraphicBufferAlloc> mGraphicBufferAlloc; - - // mFrameAvailableListener is the listener object that will be called when a - // new frame becomes available. If it is not NULL it will be called from - // queueBuffer. - sp<FrameAvailableListener> mFrameAvailableListener; - - // mSynchronousMode whether we're in synchronous mode or not - bool mSynchronousMode; - - // mAllowSynchronousMode whether we allow synchronous mode or not - const bool mAllowSynchronousMode; - - // mConnectedApi indicates the API that is currently connected to this - // SurfaceTexture. It defaults to NO_CONNECTED_API (= 0), and gets updated - // by the connect and disconnect methods. - int mConnectedApi; - - // mDequeueCondition condition used for dequeueBuffer in synchronous mode - mutable Condition mDequeueCondition; - - // mQueue is a FIFO of queued buffers used in synchronous mode - typedef Vector<int> Fifo; - Fifo mQueue; - - // mAbandoned indicates that the SurfaceTexture will no longer be used to - // consume images buffers pushed to it using the ISurfaceTexture interface. - // It is initialized to false, and set to true in the abandon method. A - // SurfaceTexture that has been abandoned will return the NO_INIT error from - // all ISurfaceTexture methods capable of returning an error. - bool mAbandoned; - - // mName is a string used to identify the SurfaceTexture in log messages. - // It is set by the setName method. - String8 mName; - // mUseFenceSync indicates whether creation of the EGL_KHR_fence_sync // extension should be used to prevent buffers from being dequeued before // it's safe for them to be written. It gets set at construction time and // never changes. const bool mUseFenceSync; - // 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. - mutable Mutex mMutex; - // mTexTarget is the GL texture target with which the GL texture object is // associated. It is set in the constructor and never changed. It is // almost always GL_TEXTURE_EXTERNAL_OES except for one use case in Android @@ -504,11 +217,6 @@ private: // browser's tile cache exceeds. const GLenum mTexTarget; - // mFrameCounter is the free running counter, incremented for every buffer queued - // with the surface Texture. - uint64_t mFrameCounter; - - }; // ---------------------------------------------------------------------------- diff --git a/include/ui/InputTransport.h b/include/ui/InputTransport.h index 95e4447c6a..1f738cdbdc 100644 --- a/include/ui/InputTransport.h +++ b/include/ui/InputTransport.h @@ -20,17 +20,13 @@ /** * Native input transport. * - * Uses anonymous shared memory as a whiteboard for sending input events from an - * InputPublisher to an InputConsumer and ensuring appropriate synchronization. - * One interesting feature is that published events can be updated in place as long as they - * have not yet been consumed. + * The InputChannel provides a mechanism for exchanging InputMessage structures across processes. * - * The InputPublisher and InputConsumer only take care of transferring event data - * over an InputChannel and sending synchronization signals. The InputDispatcher and InputQueue - * build on these abstractions to add multiplexing and queueing. + * The InputPublisher and InputConsumer each handle one end-point of an input channel. + * The InputPublisher is used by the input dispatcher to send events to the application. + * The InputConsumer is used by the application to receive events from the input dispatcher. */ -#include <semaphore.h> #include <ui/Input.h> #include <utils/Errors.h> #include <utils/Timers.h> @@ -40,11 +36,85 @@ namespace android { /* - * An input channel consists of a shared memory buffer and a pair of pipes - * used to send input messages from an InputPublisher to an InputConsumer - * across processes. Each channel has a descriptive name for debugging purposes. + * Intermediate representation used to send input events and related signals. + */ +struct InputMessage { + enum { + TYPE_KEY = 1, + TYPE_MOTION = 2, + TYPE_FINISHED = 3, + }; + + struct Header { + uint32_t type; + uint32_t padding; // 8 byte alignment for the body that follows + } header; + + union Body { + struct Key { + uint32_t seq; + nsecs_t eventTime; + int32_t deviceId; + int32_t source; + int32_t action; + int32_t flags; + int32_t keyCode; + int32_t scanCode; + int32_t metaState; + int32_t repeatCount; + nsecs_t downTime; + + inline size_t size() const { + return sizeof(Key); + } + } key; + + struct Motion { + uint32_t seq; + nsecs_t eventTime; + int32_t deviceId; + int32_t source; + int32_t action; + int32_t flags; + int32_t metaState; + int32_t buttonState; + int32_t edgeFlags; + nsecs_t downTime; + float xOffset; + float yOffset; + float xPrecision; + float yPrecision; + size_t pointerCount; + struct Pointer { + PointerProperties properties; + PointerCoords coords; + } pointers[MAX_POINTERS]; + + inline size_t size() const { + return sizeof(Motion) - sizeof(Pointer) * MAX_POINTERS + + sizeof(Pointer) * pointerCount; + } + } motion; + + struct Finished { + uint32_t seq; + bool handled; + + inline size_t size() const { + return sizeof(Finished); + } + } finished; + } body; + + bool isValid(size_t actualSize) const; + size_t size() const; +}; + +/* + * An input channel consists of a local unix domain socket used to send and receive + * input messages across processes. Each channel has a descriptive name for debugging purposes. * - * Each endpoint has its own InputChannel object that specifies its own file descriptors. + * Each endpoint has its own InputChannel object that specifies its file descriptor. * * The input channel is closed when all references to it are released. */ @@ -53,11 +123,9 @@ protected: virtual ~InputChannel(); public: - InputChannel(const String8& name, int32_t ashmemFd, int32_t receivePipeFd, - int32_t sendPipeFd); + InputChannel(const String8& name, int32_t fd); - /* Creates a pair of input channels and their underlying shared memory buffers - * and pipes. + /* Creates a pair of input channels. * * Returns OK on success. */ @@ -65,107 +133,40 @@ public: sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel); inline String8 getName() const { return mName; } - inline int32_t getAshmemFd() const { return mAshmemFd; } - inline int32_t getReceivePipeFd() const { return mReceivePipeFd; } - inline int32_t getSendPipeFd() const { return mSendPipeFd; } + inline int32_t getFd() const { return mFd; } - /* Sends a signal to the other endpoint. + /* Sends a message to the other endpoint. + * + * If the channel is full then the message is guaranteed not to have been sent at all. + * Try again after the consumer has sent a finished signal indicating that it has + * consumed some of the pending messages from the channel. * * Returns OK on success. + * Returns WOULD_BLOCK if the channel is full. * Returns DEAD_OBJECT if the channel's peer has been closed. * Other errors probably indicate that the channel is broken. */ - status_t sendSignal(char signal); + status_t sendMessage(const InputMessage* msg); - /* Receives a signal send by the other endpoint. - * (Should only call this after poll() indicates that the receivePipeFd has available input.) + /* Receives a message sent by the other endpoint. + * + * If there is no message present, try again after poll() indicates that the fd + * is readable. * * Returns OK on success. - * Returns WOULD_BLOCK if there is no signal present. + * Returns WOULD_BLOCK if there is no message present. * Returns DEAD_OBJECT if the channel's peer has been closed. * Other errors probably indicate that the channel is broken. */ - status_t receiveSignal(char* outSignal); + status_t receiveMessage(InputMessage* msg); private: String8 mName; - int32_t mAshmemFd; - int32_t mReceivePipeFd; - int32_t mSendPipeFd; + int32_t mFd; }; /* - * Private intermediate representation of input events as messages written into an - * ashmem buffer. - */ -struct InputMessage { - /* Semaphore count is set to 1 when the message is published. - * It becomes 0 transiently while the publisher updates the message. - * It becomes 0 permanently when the consumer consumes the message. - */ - sem_t semaphore; - - /* Initialized to false by the publisher. - * Set to true by the consumer when it consumes the message. - */ - bool consumed; - - int32_t type; - - struct SampleData { - nsecs_t eventTime; - PointerCoords coords[0]; // variable length - }; - - int32_t deviceId; - int32_t source; - - union { - struct { - int32_t action; - int32_t flags; - int32_t keyCode; - int32_t scanCode; - int32_t metaState; - int32_t repeatCount; - nsecs_t downTime; - nsecs_t eventTime; - } key; - - struct { - int32_t action; - int32_t flags; - int32_t metaState; - int32_t buttonState; - int32_t edgeFlags; - nsecs_t downTime; - float xOffset; - float yOffset; - float xPrecision; - float yPrecision; - size_t pointerCount; - PointerProperties pointerProperties[MAX_POINTERS]; - size_t sampleCount; - SampleData sampleData[0]; // variable length - } motion; - }; - - /* Gets the number of bytes to add to step to the next SampleData object in a motion - * event message for a given number of pointers. - */ - static inline size_t sampleDataStride(size_t pointerCount) { - return sizeof(InputMessage::SampleData) + pointerCount * sizeof(PointerCoords); - } - - /* Adds the SampleData stride to the given pointer. */ - static inline SampleData* sampleDataPtrIncrement(SampleData* ptr, size_t stride) { - return reinterpret_cast<InputMessage::SampleData*>(reinterpret_cast<char*>(ptr) + stride); - } -}; - -/* - * Publishes input events to an anonymous shared memory buffer. - * Uses atomic operations to coordinate shared access with a single concurrent consumer. + * Publishes input events to an input channel. */ class InputPublisher { public: @@ -178,26 +179,16 @@ public: /* Gets the underlying input channel. */ inline sp<InputChannel> getChannel() { return mChannel; } - /* Prepares the publisher for use. Must be called before it is used. - * Returns OK on success. - * - * This method implicitly calls reset(). */ - status_t initialize(); - - /* Resets the publisher to its initial state and unpins its ashmem buffer. - * Returns OK on success. - * - * Should be called after an event has been consumed to release resources used by the - * publisher until the next event is ready to be published. - */ - status_t reset(); - - /* Publishes a key event to the ashmem buffer. + /* Publishes a key event to the input channel. * * Returns OK on success. - * Returns INVALID_OPERATION if the publisher has not been reset. + * Returns WOULD_BLOCK if the channel is full. + * Returns DEAD_OBJECT if the channel's peer has been closed. + * Returns BAD_VALUE if seq is 0. + * Other errors probably indicate that the channel is broken. */ status_t publishKeyEvent( + uint32_t seq, int32_t deviceId, int32_t source, int32_t action, @@ -209,13 +200,16 @@ public: nsecs_t downTime, nsecs_t eventTime); - /* Publishes a motion event to the ashmem buffer. + /* Publishes a motion event to the input channel. * * Returns OK on success. - * Returns INVALID_OPERATION if the publisher has not been reset. - * Returns BAD_VALUE if pointerCount is less than 1 or greater than MAX_POINTERS. + * Returns WOULD_BLOCK if the channel is full. + * Returns DEAD_OBJECT if the channel's peer has been closed. + * Returns BAD_VALUE if seq is 0 or if pointerCount is less than 1 or greater than MAX_POINTERS. + * Other errors probably indicate that the channel is broken. */ status_t publishMotionEvent( + uint32_t seq, int32_t deviceId, int32_t source, int32_t action, @@ -233,55 +227,25 @@ public: const PointerProperties* pointerProperties, const PointerCoords* pointerCoords); - /* Appends a motion sample to a motion event unless already consumed. - * - * Returns OK on success. - * Returns INVALID_OPERATION if the current event is not a AMOTION_EVENT_ACTION_MOVE event. - * Returns FAILED_TRANSACTION if the current event has already been consumed. - * Returns NO_MEMORY if the buffer is full and no additional samples can be added. - */ - status_t appendMotionSample( - nsecs_t eventTime, - const PointerCoords* pointerCoords); - - /* Sends a dispatch signal to the consumer to inform it that a new message is available. - * - * Returns OK on success. - * Errors probably indicate that the channel is broken. - */ - status_t sendDispatchSignal(); - /* Receives the finished signal from the consumer in reply to the original dispatch signal. - * Returns whether the consumer handled the message. + * If a signal was received, returns the message sequence number, + * and whether the consumer handled the message. + * + * The returned sequence number is never 0 unless the operation failed. * * Returns OK on success. * Returns WOULD_BLOCK if there is no signal present. + * Returns DEAD_OBJECT if the channel's peer has been closed. * Other errors probably indicate that the channel is broken. */ - status_t receiveFinishedSignal(bool* outHandled); + status_t receiveFinishedSignal(uint32_t* outSeq, bool* outHandled); private: sp<InputChannel> mChannel; - - size_t mAshmemSize; - InputMessage* mSharedMessage; - bool mPinned; - bool mSemaphoreInitialized; - bool mWasDispatched; - - size_t mMotionEventPointerCount; - InputMessage::SampleData* mMotionEventSampleDataTail; - size_t mMotionEventSampleDataStride; - - status_t publishInputEvent( - int32_t type, - int32_t deviceId, - int32_t source); }; /* - * Consumes input events from an anonymous shared memory buffer. - * Uses atomic operations to coordinate shared access with a single concurrent publisher. + * Consumes input events from an input channel. */ class InputConsumer { public: @@ -294,43 +258,76 @@ public: /* Gets the underlying input channel. */ inline sp<InputChannel> getChannel() { return mChannel; } - /* Prepares the consumer for use. Must be called before it is used. */ - status_t initialize(); - - /* Consumes the input event in the buffer and copies its contents into + /* Consumes an input event from the input channel and copies its contents into * an InputEvent object created using the specified factory. - * This operation will block if the publisher is updating the event. * - * Returns OK on success. - * Returns INVALID_OPERATION if there is no currently published event. - * Returns NO_MEMORY if the event could not be created. - */ - status_t consume(InputEventFactoryInterface* factory, InputEvent** outEvent); - - /* Sends a finished signal to the publisher to inform it that the current message is - * finished processing and specifies whether the message was handled by the consumer. + * Tries to combine a series of move events into larger batches whenever possible. + * + * If consumeBatches is false, then defers consuming pending batched events if it + * is possible for additional samples to be added to them later. Call hasPendingBatch() + * to determine whether a pending batch is available to be consumed. + * + * If consumeBatches is true, then events are still batched but they are consumed + * immediately as soon as the input channel is exhausted. + * + * The returned sequence number is never 0 unless the operation failed. * * Returns OK on success. - * Errors probably indicate that the channel is broken. + * Returns WOULD_BLOCK if there is no event present. + * Returns DEAD_OBJECT if the channel's peer has been closed. + * Returns NO_MEMORY if the event could not be created. + * Other errors probably indicate that the channel is broken. */ - status_t sendFinishedSignal(bool handled); + status_t consume(InputEventFactoryInterface* factory, bool consumeBatches, + uint32_t* outSeq, InputEvent** outEvent); - /* Receives the dispatched signal from the publisher. + /* Sends a finished signal to the publisher to inform it that the message + * with the specified sequence number has finished being process and whether + * the message was handled by the consumer. * * Returns OK on success. - * Returns WOULD_BLOCK if there is no signal present. + * Returns BAD_VALUE if seq is 0. * Other errors probably indicate that the channel is broken. */ - status_t receiveDispatchSignal(); + status_t sendFinishedSignal(uint32_t seq, bool handled); + + /* Returns true if there is a pending batch. */ + bool hasPendingBatch() const; private: sp<InputChannel> mChannel; - size_t mAshmemSize; - InputMessage* mSharedMessage; + // The current input message. + InputMessage mMsg; + + // True if mMsg contains a valid input message that was deferred from the previous + // call to consume and that still needs to be handled. + bool mMsgDeferred; + + // Batched motion events per device and source. + struct Batch { + uint32_t seq; // sequence number of last input message batched in the event + MotionEvent event; + }; + Vector<Batch> mBatches; + + // Chain of batched sequence numbers. When multiple input messages are combined into + // a batch, we append a record here that associates the last sequence number in the + // batch with the previous one. When the finished signal is sent, we traverse the + // chain to individually finish all input messages that were part of the batch. + struct SeqChain { + uint32_t seq; // sequence number of batched input message + uint32_t chain; // sequence number of previous batched input message + }; + Vector<SeqChain> mSeqChains; + + ssize_t findBatch(int32_t deviceId, int32_t source) const; + status_t sendUnchainedFinishedSignal(uint32_t seq, bool handled); - void populateKeyEvent(KeyEvent* keyEvent) const; - void populateMotionEvent(MotionEvent* motionEvent) const; + static void initializeKeyEvent(KeyEvent* event, const InputMessage* msg); + static void initializeMotionEvent(MotionEvent* event, const InputMessage* msg); + static bool canAppendSamples(const MotionEvent* event, const InputMessage* msg); + static void appendSamples(MotionEvent* event, const InputMessage* msg); }; } // namespace android diff --git a/include/ui/Region.h b/include/ui/Region.h index 6c9a6203e7..f242f18eb2 100644 --- a/include/ui/Region.h +++ b/include/ui/Region.h @@ -55,43 +55,51 @@ public: void set(uint32_t w, uint32_t h); Region& orSelf(const Rect& rhs); + Region& xorSelf(const Rect& rhs); Region& andSelf(const Rect& rhs); Region& subtractSelf(const Rect& rhs); // boolean operators, applied on this Region& orSelf(const Region& rhs); + Region& xorSelf(const Region& rhs); Region& andSelf(const Region& rhs); Region& subtractSelf(const Region& rhs); // boolean operators const Region merge(const Rect& rhs) const; + const Region mergeExclusive(const Rect& rhs) const; const Region intersect(const Rect& rhs) const; const Region subtract(const Rect& rhs) const; // boolean operators const Region merge(const Region& rhs) const; + const Region mergeExclusive(const Region& rhs) const; const Region intersect(const Region& rhs) const; const Region subtract(const Region& rhs) const; // these translate rhs first Region& translateSelf(int dx, int dy); Region& orSelf(const Region& rhs, int dx, int dy); + Region& xorSelf(const Region& rhs, int dx, int dy); Region& andSelf(const Region& rhs, int dx, int dy); Region& subtractSelf(const Region& rhs, int dx, int dy); // these translate rhs first const Region translate(int dx, int dy) const; const Region merge(const Region& rhs, int dx, int dy) const; + const Region mergeExclusive(const Region& rhs, int dx, int dy) const; const Region intersect(const Region& rhs, int dx, int dy) const; const Region subtract(const Region& rhs, int dx, int dy) const; // convenience operators overloads inline const Region operator | (const Region& rhs) const; + inline const Region operator ^ (const Region& rhs) const; inline const Region operator & (const Region& rhs) const; inline const Region operator - (const Region& rhs) const; inline const Region operator + (const Point& pt) const; inline Region& operator |= (const Region& rhs); + inline Region& operator ^= (const Region& rhs); inline Region& operator &= (const Region& rhs); inline Region& operator -= (const Region& rhs); inline Region& operator += (const Point& pt); @@ -158,6 +166,9 @@ private: const Region Region::operator | (const Region& rhs) const { return merge(rhs); } +const Region Region::operator ^ (const Region& rhs) const { + return mergeExclusive(rhs); +} const Region Region::operator & (const Region& rhs) const { return intersect(rhs); } @@ -172,6 +183,9 @@ const Region Region::operator + (const Point& pt) const { Region& Region::operator |= (const Region& rhs) { return orSelf(rhs); } +Region& Region::operator ^= (const Region& rhs) { + return xorSelf(rhs); +} Region& Region::operator &= (const Region& rhs) { return andSelf(rhs); } diff --git a/include/utils/KeyedVector.h b/include/utils/KeyedVector.h index 6bcdea4ff1..fcc3bcfa00 100644 --- a/include/utils/KeyedVector.h +++ b/include/utils/KeyedVector.h @@ -66,7 +66,7 @@ public: ssize_t indexOfKey(const KEY& key) const; /*! - * modifing the array + * modifying the array */ VALUE& editValueFor(const KEY& key); diff --git a/include/utils/threads.h b/include/utils/threads.h index ab3e8cdb63..b4a8b7c0e1 100644 --- a/include/utils/threads.h +++ b/include/utils/threads.h @@ -526,6 +526,12 @@ public: // Do not call from this object's thread; will return WOULD_BLOCK in that case. status_t join(); +#ifdef HAVE_ANDROID_OS + // Return the thread's kernel ID, same as the thread itself calling gettid() or + // androidGetTid(), or -1 if the thread is not running. + pid_t getTid() const; +#endif + protected: // exitPending() returns true if requestExit() has been called. bool exitPending() const; @@ -551,8 +557,10 @@ private: volatile bool mExitPending; volatile bool mRunning; sp<Thread> mHoldSelf; -#if HAVE_ANDROID_OS - int mTid; +#ifdef HAVE_ANDROID_OS + // legacy for debugging, not used by getTid() as it is set by the child thread + // and so is not initialized until the child reaches that point + pid_t mTid; #endif }; diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index 629b89926d..b578a6ce9a 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -371,6 +371,11 @@ int IPCThreadState::getCallingUid() return mCallingUid; } +int IPCThreadState::getOrigCallingUid() +{ + return mOrigCallingUid; +} + int64_t IPCThreadState::clearCallingIdentity() { int64_t token = ((int64_t)mCallingUid<<32) | mCallingPid; @@ -641,6 +646,7 @@ IPCThreadState::IPCThreadState() { pthread_setspecific(gTLS, this); clearCaller(); + mOrigCallingUid = mCallingUid; mIn.setDataCapacity(256); mOut.setDataCapacity(256); } @@ -987,6 +993,7 @@ status_t IPCThreadState::executeCommand(int32_t cmd) mCallingPid = tr.sender_pid; mCallingUid = tr.sender_euid; + mOrigCallingUid = tr.sender_euid; int curPrio = getpriority(PRIO_PROCESS, mMyThreadId); if (gDisableBackgroundScheduling) { @@ -1045,6 +1052,7 @@ status_t IPCThreadState::executeCommand(int32_t cmd) mCallingPid = origPid; mCallingUid = origUid; + mOrigCallingUid = origUid; IF_LOG_TRANSACTIONS() { TextOutput::Bundle _b(alog); diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp index 33b305dccb..1750640fc1 100644 --- a/libs/binder/IServiceManager.cpp +++ b/libs/binder/IServiceManager.cpp @@ -151,12 +151,14 @@ public: return reply.readStrongBinder(); } - virtual status_t addService(const String16& name, const sp<IBinder>& service) + virtual status_t addService(const String16& name, const sp<IBinder>& service, + bool allowIsolated) { Parcel data, reply; data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); data.writeString16(name); data.writeStrongBinder(service); + data.writeInt32(allowIsolated ? 1 : 0); status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply); return err == NO_ERROR ? reply.readExceptionCode() : err; } diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk index b6f5b9e8e8..2f4ac62944 100644 --- a/libs/gui/Android.mk +++ b/libs/gui/Android.mk @@ -3,6 +3,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ BitTube.cpp \ + BufferQueue.cpp \ DisplayEventReceiver.cpp \ IDisplayEventConnection.cpp \ ISensorEventConnection.cpp \ diff --git a/libs/gui/BitTube.cpp b/libs/gui/BitTube.cpp index 785da39b95..55f4178421 100644 --- a/libs/gui/BitTube.cpp +++ b/libs/gui/BitTube.cpp @@ -17,8 +17,9 @@ #include <stdint.h> #include <sys/types.h> -#include <unistd.h> #include <fcntl.h> +#include <signal.h> +#include <unistd.h> #include <utils/Errors.h> @@ -38,6 +39,8 @@ BitTube::BitTube() mSendFd = fds[1]; fcntl(mReceiveFd, F_SETFL, O_NONBLOCK); fcntl(mSendFd, F_SETFL, O_NONBLOCK); + // ignore SIGPIPE, we handle write errors through EPIPE instead + signal(SIGPIPE, SIG_IGN); } else { mReceiveFd = -errno; ALOGE("BitTube: pipe creation failed (%s)", strerror(-mReceiveFd)); diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp new file mode 100644 index 0000000000..c7e2c0f21f --- /dev/null +++ b/libs/gui/BufferQueue.cpp @@ -0,0 +1,722 @@ +/* + * Copyright (C) 2012 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 LOG_TAG "BufferQueue" + +#define GL_GLEXT_PROTOTYPES +#define EGL_EGLEXT_PROTOTYPES + +#include <EGL/egl.h> +#include <EGL/eglext.h> + +#include <gui/BufferQueue.h> +#include <private/gui/ComposerService.h> +#include <surfaceflinger/ISurfaceComposer.h> + +#include <utils/Log.h> + +// This compile option causes SurfaceTexture to return the buffer that is currently +// attached to the GL texture from dequeueBuffer when no other buffers are +// available. It requires the drivers (Gralloc, GL, OMX IL, and Camera) to do +// implicit cross-process synchronization to prevent the buffer from being +// written to before the buffer has (a) been detached from the GL texture and +// (b) all GL reads from the buffer have completed. +#ifdef ALLOW_DEQUEUE_CURRENT_BUFFER +#define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER true +#warning "ALLOW_DEQUEUE_CURRENT_BUFFER enabled" +#else +#define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER false +#endif + +// Macros for including the BufferQueue name in log messages +#define ST_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__) +#define ST_LOGD(x, ...) ALOGD("[%s] "x, mName.string(), ##__VA_ARGS__) +#define ST_LOGI(x, ...) ALOGI("[%s] "x, mName.string(), ##__VA_ARGS__) +#define ST_LOGW(x, ...) ALOGW("[%s] "x, mName.string(), ##__VA_ARGS__) +#define ST_LOGE(x, ...) ALOGE("[%s] "x, mName.string(), ##__VA_ARGS__) + +namespace android { + +// Get an ID that's unique within this process. +static int32_t createProcessUniqueId() { + static volatile int32_t globalCounter = 0; + return android_atomic_inc(&globalCounter); +} + +BufferQueue::BufferQueue( bool allowSynchronousMode ) : + mDefaultWidth(1), + mDefaultHeight(1), + mPixelFormat(PIXEL_FORMAT_RGBA_8888), + mBufferCount(MIN_ASYNC_BUFFER_SLOTS), + mClientBufferCount(0), + mServerBufferCount(MIN_ASYNC_BUFFER_SLOTS), + mCurrentTexture(INVALID_BUFFER_SLOT), + mNextTransform(0), + mNextScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), + mSynchronousMode(false), + mAllowSynchronousMode(allowSynchronousMode), + mConnectedApi(NO_CONNECTED_API), + mAbandoned(false), + mFrameCounter(0) +{ + // Choose a name using the PID and a process-unique ID. + mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId()); + + ST_LOGV("BufferQueue"); + sp<ISurfaceComposer> composer(ComposerService::getComposerService()); + mGraphicBufferAlloc = composer->createGraphicBufferAlloc(); + mNextCrop.makeInvalid(); +} + +BufferQueue::~BufferQueue() { + ST_LOGV("~BufferQueue"); +} + +status_t BufferQueue::setBufferCountServerLocked(int bufferCount) { + if (bufferCount > NUM_BUFFER_SLOTS) + return BAD_VALUE; + + // special-case, nothing to do + if (bufferCount == mBufferCount) + return OK; + + if (!mClientBufferCount && + bufferCount >= mBufferCount) { + // easy, we just have more buffers + mBufferCount = bufferCount; + mServerBufferCount = bufferCount; + mDequeueCondition.signal(); + } else { + // we're here because we're either + // - reducing the number of available buffers + // - or there is a client-buffer-count in effect + + // less than 2 buffers is never allowed + if (bufferCount < 2) + return BAD_VALUE; + + // when there is non client-buffer-count in effect, the client is not + // allowed to dequeue more than one buffer at a time, + // so the next time they dequeue a buffer, we know that they don't + // own one. the actual resizing will happen during the next + // dequeueBuffer. + + mServerBufferCount = bufferCount; + } + return OK; +} + +status_t BufferQueue::setBufferCount(int bufferCount) { + ST_LOGV("setBufferCount: count=%d", bufferCount); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + ST_LOGE("setBufferCount: SurfaceTexture has been abandoned!"); + return NO_INIT; + } + if (bufferCount > NUM_BUFFER_SLOTS) { + ST_LOGE("setBufferCount: bufferCount larger than slots available"); + return BAD_VALUE; + } + + // Error out if the user has dequeued buffers + for (int i=0 ; i<mBufferCount ; i++) { + if (mSlots[i].mBufferState == BufferSlot::DEQUEUED) { + ST_LOGE("setBufferCount: client owns some buffers"); + return -EINVAL; + } + } + + const int minBufferSlots = mSynchronousMode ? + MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS; + if (bufferCount == 0) { + mClientBufferCount = 0; + bufferCount = (mServerBufferCount >= minBufferSlots) ? + mServerBufferCount : minBufferSlots; + return setBufferCountServerLocked(bufferCount); + } + + if (bufferCount < minBufferSlots) { + ST_LOGE("setBufferCount: requested buffer count (%d) is less than " + "minimum (%d)", bufferCount, minBufferSlots); + return BAD_VALUE; + } + + // here we're guaranteed that the client doesn't have dequeued buffers + // and will release all of its buffer references. + freeAllBuffersLocked(); + mBufferCount = bufferCount; + mClientBufferCount = bufferCount; + mCurrentTexture = INVALID_BUFFER_SLOT; + mQueue.clear(); + mDequeueCondition.signal(); + return OK; +} + +status_t BufferQueue::requestBuffer(int slot, sp<GraphicBuffer>* buf) { + ST_LOGV("requestBuffer: slot=%d", slot); + Mutex::Autolock lock(mMutex); + if (mAbandoned) { + ST_LOGE("requestBuffer: SurfaceTexture has been abandoned!"); + return NO_INIT; + } + if (slot < 0 || mBufferCount <= slot) { + ST_LOGE("requestBuffer: slot index out of range [0, %d]: %d", + mBufferCount, slot); + return BAD_VALUE; + } + mSlots[slot].mRequestBufferCalled = true; + *buf = mSlots[slot].mGraphicBuffer; + return NO_ERROR; +} + +status_t BufferQueue::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, + uint32_t format, uint32_t usage) { + ST_LOGV("dequeueBuffer: w=%d h=%d fmt=%#x usage=%#x", w, h, format, usage); + + if ((w && !h) || (!w && h)) { + ST_LOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h); + return BAD_VALUE; + } + + status_t returnFlags(OK); + EGLDisplay dpy = EGL_NO_DISPLAY; + EGLSyncKHR fence = EGL_NO_SYNC_KHR; + + { // Scope for the lock + Mutex::Autolock lock(mMutex); + + int found = -1; + int foundSync = -1; + int dequeuedCount = 0; + bool tryAgain = true; + while (tryAgain) { + if (mAbandoned) { + ST_LOGE("dequeueBuffer: SurfaceTexture has been abandoned!"); + return NO_INIT; + } + + // We need to wait for the FIFO to drain if the number of buffer + // needs to change. + // + // The condition "number of buffers needs to change" is true if + // - the client doesn't care about how many buffers there are + // - AND the actual number of buffer is different from what was + // set in the last setBufferCountServer() + // - OR - + // setBufferCountServer() was set to a value incompatible with + // the synchronization mode (for instance because the sync mode + // changed since) + // + // As long as this condition is true AND the FIFO is not empty, we + // wait on mDequeueCondition. + + const int minBufferCountNeeded = mSynchronousMode ? + MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS; + + const bool numberOfBuffersNeedsToChange = !mClientBufferCount && + ((mServerBufferCount != mBufferCount) || + (mServerBufferCount < minBufferCountNeeded)); + + if (!mQueue.isEmpty() && numberOfBuffersNeedsToChange) { + // wait for the FIFO to drain + mDequeueCondition.wait(mMutex); + // NOTE: we continue here because we need to reevaluate our + // whole state (eg: we could be abandoned or disconnected) + continue; + } + + if (numberOfBuffersNeedsToChange) { + // here we're guaranteed that mQueue is empty + freeAllBuffersLocked(); + mBufferCount = mServerBufferCount; + if (mBufferCount < minBufferCountNeeded) + mBufferCount = minBufferCountNeeded; + mCurrentTexture = INVALID_BUFFER_SLOT; + returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS; + } + + // look for a free buffer to give to the client + found = INVALID_BUFFER_SLOT; + foundSync = INVALID_BUFFER_SLOT; + dequeuedCount = 0; + for (int i = 0; i < mBufferCount; i++) { + const int state = mSlots[i].mBufferState; + if (state == BufferSlot::DEQUEUED) { + dequeuedCount++; + } + + // if buffer is FREE it CANNOT be current + ALOGW_IF((state == BufferSlot::FREE) && (mCurrentTexture==i), + "dequeueBuffer: buffer %d is both FREE and current!", + i); + + if (FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER) { + if (state == BufferSlot::FREE || i == mCurrentTexture) { + foundSync = i; + if (i != mCurrentTexture) { + found = i; + break; + } + } + } else { + if (state == BufferSlot::FREE) { + /* We return the oldest of the free buffers to avoid + * stalling the producer if possible. This is because + * the consumer may still have pending reads of the + * buffers in flight. + */ + bool isOlder = mSlots[i].mFrameNumber < + mSlots[found].mFrameNumber; + if (found < 0 || isOlder) { + foundSync = i; + found = i; + } + } + } + } + + // clients are not allowed to dequeue more than one buffer + // if they didn't set a buffer count. + if (!mClientBufferCount && dequeuedCount) { + ST_LOGE("dequeueBuffer: can't dequeue multiple buffers without " + "setting the buffer count"); + return -EINVAL; + } + + // See whether a buffer has been queued since the last + // setBufferCount so we know whether to perform the + // MIN_UNDEQUEUED_BUFFERS check below. + bool bufferHasBeenQueued = mCurrentTexture != INVALID_BUFFER_SLOT; + if (bufferHasBeenQueued) { + // make sure the client is not trying to dequeue more buffers + // than allowed. + const int avail = mBufferCount - (dequeuedCount+1); + if (avail < (MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode))) { + ST_LOGE("dequeueBuffer: MIN_UNDEQUEUED_BUFFERS=%d exceeded " + "(dequeued=%d)", + MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode), + dequeuedCount); + return -EBUSY; + } + } + + // we're in synchronous mode and didn't find a buffer, we need to + // wait for some buffers to be consumed + tryAgain = mSynchronousMode && (foundSync == INVALID_BUFFER_SLOT); + if (tryAgain) { + mDequeueCondition.wait(mMutex); + } + } + + if (mSynchronousMode && found == INVALID_BUFFER_SLOT) { + // foundSync guaranteed to be != INVALID_BUFFER_SLOT + found = foundSync; + } + + if (found == INVALID_BUFFER_SLOT) { + // This should not happen. + ST_LOGE("dequeueBuffer: no available buffer slots"); + return -EBUSY; + } + + const int buf = found; + *outBuf = found; + + const bool useDefaultSize = !w && !h; + if (useDefaultSize) { + // use the default size + w = mDefaultWidth; + h = mDefaultHeight; + } + + const bool updateFormat = (format != 0); + if (!updateFormat) { + // keep the current (or default) format + format = mPixelFormat; + } + + // buffer is now in DEQUEUED (but can also be current at the same time, + // if we're in synchronous mode) + mSlots[buf].mBufferState = BufferSlot::DEQUEUED; + + const sp<GraphicBuffer>& buffer(mSlots[buf].mGraphicBuffer); + if ((buffer == NULL) || + (uint32_t(buffer->width) != w) || + (uint32_t(buffer->height) != h) || + (uint32_t(buffer->format) != format) || + ((uint32_t(buffer->usage) & usage) != usage)) + { + usage |= GraphicBuffer::USAGE_HW_TEXTURE; + status_t error; + sp<GraphicBuffer> graphicBuffer( + mGraphicBufferAlloc->createGraphicBuffer( + w, h, format, usage, &error)); + if (graphicBuffer == 0) { + ST_LOGE("dequeueBuffer: SurfaceComposer::createGraphicBuffer " + "failed"); + return error; + } + if (updateFormat) { + mPixelFormat = format; + } + mSlots[buf].mGraphicBuffer = graphicBuffer; + mSlots[buf].mRequestBufferCalled = false; + mSlots[buf].mFence = EGL_NO_SYNC_KHR; + if (mSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(mSlots[buf].mEglDisplay, + mSlots[buf].mEglImage); + mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR; + mSlots[buf].mEglDisplay = EGL_NO_DISPLAY; + } + if (mCurrentTexture == buf) { + // The current texture no longer references the buffer in this slot + // since we just allocated a new buffer. + mCurrentTexture = INVALID_BUFFER_SLOT; + } + returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION; + } + + dpy = mSlots[buf].mEglDisplay; + fence = mSlots[buf].mFence; + mSlots[buf].mFence = EGL_NO_SYNC_KHR; + } + + if (fence != EGL_NO_SYNC_KHR) { + EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000); + // If something goes wrong, log the error, but return the buffer without + // synchronizing access to it. It's too late at this point to abort the + // dequeue operation. + if (result == EGL_FALSE) { + ALOGE("dequeueBuffer: error waiting for fence: %#x", eglGetError()); + } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { + ALOGE("dequeueBuffer: timeout waiting for fence"); + } + eglDestroySyncKHR(dpy, fence); + } + + ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", *outBuf, + mSlots[*outBuf].mGraphicBuffer->handle, returnFlags); + + return returnFlags; +} + +status_t BufferQueue::setSynchronousMode(bool enabled) { + ST_LOGV("setSynchronousMode: enabled=%d", enabled); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + ST_LOGE("setSynchronousMode: SurfaceTexture has been abandoned!"); + return NO_INIT; + } + + status_t err = OK; + if (!mAllowSynchronousMode && enabled) + return err; + + if (!enabled) { + // going to asynchronous mode, drain the queue + err = drainQueueLocked(); + if (err != NO_ERROR) + return err; + } + + if (mSynchronousMode != enabled) { + // - if we're going to asynchronous mode, the queue is guaranteed to be + // empty here + // - if the client set the number of buffers, we're guaranteed that + // we have at least 3 (because we don't allow less) + mSynchronousMode = enabled; + mDequeueCondition.signal(); + } + return err; +} + +status_t BufferQueue::queueBuffer(int buf, int64_t timestamp, + uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) { + ST_LOGV("queueBuffer: slot=%d time=%lld", buf, timestamp); + + sp<FrameAvailableListener> listener; + + { // scope for the lock + Mutex::Autolock lock(mMutex); + if (mAbandoned) { + ST_LOGE("queueBuffer: SurfaceTexture has been abandoned!"); + return NO_INIT; + } + if (buf < 0 || buf >= mBufferCount) { + ST_LOGE("queueBuffer: slot index out of range [0, %d]: %d", + mBufferCount, buf); + return -EINVAL; + } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { + ST_LOGE("queueBuffer: slot %d is not owned by the client " + "(state=%d)", buf, mSlots[buf].mBufferState); + return -EINVAL; + } else if (buf == mCurrentTexture) { + ST_LOGE("queueBuffer: slot %d is current!", buf); + return -EINVAL; + } else if (!mSlots[buf].mRequestBufferCalled) { + ST_LOGE("queueBuffer: slot %d was enqueued without requesting a " + "buffer", buf); + return -EINVAL; + } + + if (mSynchronousMode) { + // In synchronous mode we queue all buffers in a FIFO. + mQueue.push_back(buf); + + // Synchronous mode always signals that an additional frame should + // be consumed. + listener = mFrameAvailableListener; + } else { + // In asynchronous mode we only keep the most recent buffer. + if (mQueue.empty()) { + mQueue.push_back(buf); + + // Asynchronous mode only signals that a frame should be + // consumed if no previous frame was pending. If a frame were + // pending then the consumer would have already been notified. + listener = mFrameAvailableListener; + } else { + Fifo::iterator front(mQueue.begin()); + // buffer currently queued is freed + mSlots[*front].mBufferState = BufferSlot::FREE; + // and we record the new buffer index in the queued list + *front = buf; + } + } + + mSlots[buf].mBufferState = BufferSlot::QUEUED; + mSlots[buf].mCrop = mNextCrop; + mSlots[buf].mTransform = mNextTransform; + mSlots[buf].mScalingMode = mNextScalingMode; + mSlots[buf].mTimestamp = timestamp; + mFrameCounter++; + mSlots[buf].mFrameNumber = mFrameCounter; + + mDequeueCondition.signal(); + + *outWidth = mDefaultWidth; + *outHeight = mDefaultHeight; + *outTransform = 0; + } // scope for the lock + + // call back without lock held + if (listener != 0) { + listener->onFrameAvailable(); + } + return OK; +} + +void BufferQueue::cancelBuffer(int buf) { + ST_LOGV("cancelBuffer: slot=%d", buf); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + ST_LOGW("cancelBuffer: BufferQueue has been abandoned!"); + return; + } + + if (buf < 0 || buf >= mBufferCount) { + ST_LOGE("cancelBuffer: slot index out of range [0, %d]: %d", + mBufferCount, buf); + return; + } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { + ST_LOGE("cancelBuffer: slot %d is not owned by the client (state=%d)", + buf, mSlots[buf].mBufferState); + return; + } + mSlots[buf].mBufferState = BufferSlot::FREE; + mSlots[buf].mFrameNumber = 0; + mDequeueCondition.signal(); +} + +status_t BufferQueue::setCrop(const Rect& crop) { + ST_LOGV("setCrop: crop=[%d,%d,%d,%d]", crop.left, crop.top, crop.right, + crop.bottom); + + Mutex::Autolock lock(mMutex); + if (mAbandoned) { + ST_LOGE("setCrop: BufferQueue has been abandoned!"); + return NO_INIT; + } + mNextCrop = crop; + return OK; +} + +status_t BufferQueue::setTransform(uint32_t transform) { + ST_LOGV("setTransform: xform=%#x", transform); + Mutex::Autolock lock(mMutex); + if (mAbandoned) { + ST_LOGE("setTransform: BufferQueue has been abandoned!"); + return NO_INIT; + } + mNextTransform = transform; + return OK; +} + +status_t BufferQueue::setScalingMode(int mode) { + ST_LOGV("setScalingMode: mode=%d", mode); + + switch (mode) { + case NATIVE_WINDOW_SCALING_MODE_FREEZE: + case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: + break; + default: + ST_LOGE("unknown scaling mode: %d", mode); + return BAD_VALUE; + } + + Mutex::Autolock lock(mMutex); + mNextScalingMode = mode; + return OK; +} + +status_t BufferQueue::connect(int api, + uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) { + ST_LOGV("connect: api=%d", api); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + ST_LOGE("connect: BufferQueue has been abandoned!"); + return NO_INIT; + } + + int err = NO_ERROR; + switch (api) { + case NATIVE_WINDOW_API_EGL: + case NATIVE_WINDOW_API_CPU: + case NATIVE_WINDOW_API_MEDIA: + case NATIVE_WINDOW_API_CAMERA: + if (mConnectedApi != NO_CONNECTED_API) { + ST_LOGE("connect: already connected (cur=%d, req=%d)", + mConnectedApi, api); + err = -EINVAL; + } else { + mConnectedApi = api; + *outWidth = mDefaultWidth; + *outHeight = mDefaultHeight; + *outTransform = 0; + } + break; + default: + err = -EINVAL; + break; + } + return err; +} + +status_t BufferQueue::disconnect(int api) { + ST_LOGV("disconnect: api=%d", api); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + // it is not really an error to disconnect after the surface + // has been abandoned, it should just be a no-op. + return NO_ERROR; + } + + int err = NO_ERROR; + switch (api) { + case NATIVE_WINDOW_API_EGL: + case NATIVE_WINDOW_API_CPU: + case NATIVE_WINDOW_API_MEDIA: + case NATIVE_WINDOW_API_CAMERA: + if (mConnectedApi == api) { + drainQueueAndFreeBuffersLocked(); + mConnectedApi = NO_CONNECTED_API; + mNextCrop.makeInvalid(); + mNextScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE; + mNextTransform = 0; + mDequeueCondition.signal(); + } else { + ST_LOGE("disconnect: connected to another api (cur=%d, req=%d)", + mConnectedApi, api); + err = -EINVAL; + } + break; + default: + ST_LOGE("disconnect: unknown API %d", api); + err = -EINVAL; + break; + } + return err; +} + +void BufferQueue::freeBufferLocked(int i) { + mSlots[i].mGraphicBuffer = 0; + mSlots[i].mBufferState = BufferSlot::FREE; + mSlots[i].mFrameNumber = 0; + if (mSlots[i].mEglImage != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(mSlots[i].mEglDisplay, mSlots[i].mEglImage); + mSlots[i].mEglImage = EGL_NO_IMAGE_KHR; + mSlots[i].mEglDisplay = EGL_NO_DISPLAY; + } +} + +void BufferQueue::freeAllBuffersLocked() { + ALOGW_IF(!mQueue.isEmpty(), + "freeAllBuffersLocked called but mQueue is not empty"); + mCurrentTexture = INVALID_BUFFER_SLOT; + for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { + freeBufferLocked(i); + } +} + +void BufferQueue::freeAllBuffersExceptHeadLocked() { + ALOGW_IF(!mQueue.isEmpty(), + "freeAllBuffersExceptCurrentLocked called but mQueue is not empty"); + int head = -1; + if (!mQueue.empty()) { + Fifo::iterator front(mQueue.begin()); + head = *front; + } + mCurrentTexture = INVALID_BUFFER_SLOT; + for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { + if (i != head) { + freeBufferLocked(i); + } + } +} + +status_t BufferQueue::drainQueueLocked() { + while (mSynchronousMode && !mQueue.isEmpty()) { + mDequeueCondition.wait(mMutex); + if (mAbandoned) { + ST_LOGE("drainQueueLocked: BufferQueue has been abandoned!"); + return NO_INIT; + } + if (mConnectedApi == NO_CONNECTED_API) { + ST_LOGE("drainQueueLocked: BufferQueue is not connected!"); + return NO_INIT; + } + } + return NO_ERROR; +} + +status_t BufferQueue::drainQueueAndFreeBuffersLocked() { + status_t err = drainQueueLocked(); + if (err == NO_ERROR) { + if (mSynchronousMode) { + freeAllBuffersLocked(); + } else { + freeAllBuffersExceptHeadLocked(); + } + } + return err; +} + +}; // namespace android diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp index 3b3ccaaf74..6a4763d5a7 100644 --- a/libs/gui/DisplayEventReceiver.cpp +++ b/libs/gui/DisplayEventReceiver.cpp @@ -80,7 +80,13 @@ status_t DisplayEventReceiver::requestNextVsync() { ssize_t DisplayEventReceiver::getEvents(DisplayEventReceiver::Event* events, size_t count) { - ssize_t size = mDataChannel->read(events, sizeof(events[0])*count); + return DisplayEventReceiver::getEvents(mDataChannel, events, count); +} + +ssize_t DisplayEventReceiver::getEvents(const sp<BitTube>& dataChannel, + Event* events, size_t count) +{ + ssize_t size = dataChannel->read(events, sizeof(events[0])*count); ALOGE_IF(size<0, "DisplayEventReceiver::getEvents error (%s)", strerror(-size)); diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp index 3abe84ad8b..be1bcd1a81 100644 --- a/libs/gui/SurfaceTexture.cpp +++ b/libs/gui/SurfaceTexture.cpp @@ -38,19 +38,6 @@ #include <utils/Log.h> #include <utils/String8.h> -// This compile option causes SurfaceTexture to return the buffer that is currently -// attached to the GL texture from dequeueBuffer when no other buffers are -// available. It requires the drivers (Gralloc, GL, OMX IL, and Camera) to do -// implicit cross-process synchronization to prevent the buffer from being -// written to before the buffer has (a) been detached from the GL texture and -// (b) all GL reads from the buffer have completed. -#ifdef ALLOW_DEQUEUE_CURRENT_BUFFER -#define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER true -#warning "ALLOW_DEQUEUE_CURRENT_BUFFER enabled" -#else -#define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER false -#endif - // This compile option makes SurfaceTexture use the EGL_KHR_fence_sync extension // to synchronize access to the buffers. It will cause dequeueBuffer to stall, // waiting for the GL reads for the buffer being dequeued to complete before @@ -110,44 +97,22 @@ static float mtxRot270[16] = { static void mtxMul(float out[16], const float a[16], const float b[16]); -// Get an ID that's unique within this process. -static int32_t createProcessUniqueId() { - static volatile int32_t globalCounter = 0; - return android_atomic_inc(&globalCounter); -} SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode, GLenum texTarget, bool useFenceSync) : - mDefaultWidth(1), - mDefaultHeight(1), - mPixelFormat(PIXEL_FORMAT_RGBA_8888), - mBufferCount(MIN_ASYNC_BUFFER_SLOTS), - mClientBufferCount(0), - mServerBufferCount(MIN_ASYNC_BUFFER_SLOTS), - mCurrentTexture(INVALID_BUFFER_SLOT), + BufferQueue(allowSynchronousMode), mCurrentTransform(0), mCurrentTimestamp(0), - mNextTransform(0), - mNextScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), mTexName(tex), - mSynchronousMode(false), - mAllowSynchronousMode(allowSynchronousMode), - mConnectedApi(NO_CONNECTED_API), - mAbandoned(false), #ifdef USE_FENCE_SYNC mUseFenceSync(useFenceSync), #else mUseFenceSync(false), #endif - mTexTarget(texTarget), - mFrameCounter(0) { - // Choose a name using the PID and a process-unique ID. - mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId()); + mTexTarget(texTarget) +{ ST_LOGV("SurfaceTexture"); - sp<ISurfaceComposer> composer(ComposerService::getComposerService()); - mGraphicBufferAlloc = composer->createGraphicBufferAlloc(); - mNextCrop.makeInvalid(); memcpy(mCurrentTransformMatrix, mtxIdentity, sizeof(mCurrentTransformMatrix)); } @@ -157,91 +122,11 @@ SurfaceTexture::~SurfaceTexture() { freeAllBuffersLocked(); } -status_t SurfaceTexture::setBufferCountServerLocked(int bufferCount) { - if (bufferCount > NUM_BUFFER_SLOTS) - return BAD_VALUE; - - // special-case, nothing to do - if (bufferCount == mBufferCount) - return OK; - - if (!mClientBufferCount && - bufferCount >= mBufferCount) { - // easy, we just have more buffers - mBufferCount = bufferCount; - mServerBufferCount = bufferCount; - mDequeueCondition.signal(); - } else { - // we're here because we're either - // - reducing the number of available buffers - // - or there is a client-buffer-count in effect - - // less than 2 buffers is never allowed - if (bufferCount < 2) - return BAD_VALUE; - - // when there is non client-buffer-count in effect, the client is not - // allowed to dequeue more than one buffer at a time, - // so the next time they dequeue a buffer, we know that they don't - // own one. the actual resizing will happen during the next - // dequeueBuffer. - - mServerBufferCount = bufferCount; - } - return OK; -} - status_t SurfaceTexture::setBufferCountServer(int bufferCount) { Mutex::Autolock lock(mMutex); return setBufferCountServerLocked(bufferCount); } -status_t SurfaceTexture::setBufferCount(int bufferCount) { - ST_LOGV("setBufferCount: count=%d", bufferCount); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - ST_LOGE("setBufferCount: SurfaceTexture has been abandoned!"); - return NO_INIT; - } - if (bufferCount > NUM_BUFFER_SLOTS) { - ST_LOGE("setBufferCount: bufferCount larger than slots available"); - return BAD_VALUE; - } - - // Error out if the user has dequeued buffers - for (int i=0 ; i<mBufferCount ; i++) { - if (mSlots[i].mBufferState == BufferSlot::DEQUEUED) { - ST_LOGE("setBufferCount: client owns some buffers"); - return -EINVAL; - } - } - - const int minBufferSlots = mSynchronousMode ? - MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS; - if (bufferCount == 0) { - mClientBufferCount = 0; - bufferCount = (mServerBufferCount >= minBufferSlots) ? - mServerBufferCount : minBufferSlots; - return setBufferCountServerLocked(bufferCount); - } - - if (bufferCount < minBufferSlots) { - ST_LOGE("setBufferCount: requested buffer count (%d) is less than " - "minimum (%d)", bufferCount, minBufferSlots); - return BAD_VALUE; - } - - // here we're guaranteed that the client doesn't have dequeued buffers - // and will release all of its buffer references. - freeAllBuffersLocked(); - mBufferCount = bufferCount; - mClientBufferCount = bufferCount; - mCurrentTexture = INVALID_BUFFER_SLOT; - mQueue.clear(); - mDequeueCondition.signal(); - return OK; -} status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h) { @@ -258,496 +143,6 @@ status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h) return OK; } -status_t SurfaceTexture::requestBuffer(int slot, sp<GraphicBuffer>* buf) { - ST_LOGV("requestBuffer: slot=%d", slot); - Mutex::Autolock lock(mMutex); - if (mAbandoned) { - ST_LOGE("requestBuffer: SurfaceTexture has been abandoned!"); - return NO_INIT; - } - if (slot < 0 || mBufferCount <= slot) { - ST_LOGE("requestBuffer: slot index out of range [0, %d]: %d", - mBufferCount, slot); - return BAD_VALUE; - } - mSlots[slot].mRequestBufferCalled = true; - *buf = mSlots[slot].mGraphicBuffer; - return NO_ERROR; -} - -status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, - uint32_t format, uint32_t usage) { - ST_LOGV("dequeueBuffer: w=%d h=%d fmt=%#x usage=%#x", w, h, format, usage); - - if ((w && !h) || (!w && h)) { - ST_LOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h); - return BAD_VALUE; - } - - status_t returnFlags(OK); - EGLDisplay dpy = EGL_NO_DISPLAY; - EGLSyncKHR fence = EGL_NO_SYNC_KHR; - - { // Scope for the lock - Mutex::Autolock lock(mMutex); - - int found = -1; - int foundSync = -1; - int dequeuedCount = 0; - bool tryAgain = true; - while (tryAgain) { - if (mAbandoned) { - ST_LOGE("dequeueBuffer: SurfaceTexture has been abandoned!"); - return NO_INIT; - } - - // We need to wait for the FIFO to drain if the number of buffer - // needs to change. - // - // The condition "number of buffers needs to change" is true if - // - the client doesn't care about how many buffers there are - // - AND the actual number of buffer is different from what was - // set in the last setBufferCountServer() - // - OR - - // setBufferCountServer() was set to a value incompatible with - // the synchronization mode (for instance because the sync mode - // changed since) - // - // As long as this condition is true AND the FIFO is not empty, we - // wait on mDequeueCondition. - - const int minBufferCountNeeded = mSynchronousMode ? - MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS; - - const bool numberOfBuffersNeedsToChange = !mClientBufferCount && - ((mServerBufferCount != mBufferCount) || - (mServerBufferCount < minBufferCountNeeded)); - - if (!mQueue.isEmpty() && numberOfBuffersNeedsToChange) { - // wait for the FIFO to drain - mDequeueCondition.wait(mMutex); - // NOTE: we continue here because we need to reevaluate our - // whole state (eg: we could be abandoned or disconnected) - continue; - } - - if (numberOfBuffersNeedsToChange) { - // here we're guaranteed that mQueue is empty - freeAllBuffersLocked(); - mBufferCount = mServerBufferCount; - if (mBufferCount < minBufferCountNeeded) - mBufferCount = minBufferCountNeeded; - mCurrentTexture = INVALID_BUFFER_SLOT; - returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS; - } - - // look for a free buffer to give to the client - found = INVALID_BUFFER_SLOT; - foundSync = INVALID_BUFFER_SLOT; - dequeuedCount = 0; - for (int i = 0; i < mBufferCount; i++) { - const int state = mSlots[i].mBufferState; - if (state == BufferSlot::DEQUEUED) { - dequeuedCount++; - } - - // if buffer is FREE it CANNOT be current - ALOGW_IF((state == BufferSlot::FREE) && (mCurrentTexture==i), - "dequeueBuffer: buffer %d is both FREE and current!", - i); - - if (FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER) { - if (state == BufferSlot::FREE || i == mCurrentTexture) { - foundSync = i; - if (i != mCurrentTexture) { - found = i; - break; - } - } - } else { - if (state == BufferSlot::FREE) { - /* We return the oldest of the free buffers to avoid - * stalling the producer if possible. This is because - * the consumer may still have pending reads of the - * buffers in flight. - */ - bool isOlder = mSlots[i].mFrameNumber < - mSlots[found].mFrameNumber; - if (found < 0 || isOlder) { - foundSync = i; - found = i; - } - } - } - } - - // clients are not allowed to dequeue more than one buffer - // if they didn't set a buffer count. - if (!mClientBufferCount && dequeuedCount) { - ST_LOGE("dequeueBuffer: can't dequeue multiple buffers without " - "setting the buffer count"); - return -EINVAL; - } - - // See whether a buffer has been queued since the last - // setBufferCount so we know whether to perform the - // MIN_UNDEQUEUED_BUFFERS check below. - bool bufferHasBeenQueued = mCurrentTexture != INVALID_BUFFER_SLOT; - if (bufferHasBeenQueued) { - // make sure the client is not trying to dequeue more buffers - // than allowed. - const int avail = mBufferCount - (dequeuedCount+1); - if (avail < (MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode))) { - ST_LOGE("dequeueBuffer: MIN_UNDEQUEUED_BUFFERS=%d exceeded " - "(dequeued=%d)", - MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode), - dequeuedCount); - return -EBUSY; - } - } - - // we're in synchronous mode and didn't find a buffer, we need to - // wait for some buffers to be consumed - tryAgain = mSynchronousMode && (foundSync == INVALID_BUFFER_SLOT); - if (tryAgain) { - mDequeueCondition.wait(mMutex); - } - } - - if (mSynchronousMode && found == INVALID_BUFFER_SLOT) { - // foundSync guaranteed to be != INVALID_BUFFER_SLOT - found = foundSync; - } - - if (found == INVALID_BUFFER_SLOT) { - // This should not happen. - ST_LOGE("dequeueBuffer: no available buffer slots"); - return -EBUSY; - } - - const int buf = found; - *outBuf = found; - - const bool useDefaultSize = !w && !h; - if (useDefaultSize) { - // use the default size - w = mDefaultWidth; - h = mDefaultHeight; - } - - const bool updateFormat = (format != 0); - if (!updateFormat) { - // keep the current (or default) format - format = mPixelFormat; - } - - // buffer is now in DEQUEUED (but can also be current at the same time, - // if we're in synchronous mode) - mSlots[buf].mBufferState = BufferSlot::DEQUEUED; - - const sp<GraphicBuffer>& buffer(mSlots[buf].mGraphicBuffer); - if ((buffer == NULL) || - (uint32_t(buffer->width) != w) || - (uint32_t(buffer->height) != h) || - (uint32_t(buffer->format) != format) || - ((uint32_t(buffer->usage) & usage) != usage)) - { - usage |= GraphicBuffer::USAGE_HW_TEXTURE; - status_t error; - sp<GraphicBuffer> graphicBuffer( - mGraphicBufferAlloc->createGraphicBuffer( - w, h, format, usage, &error)); - if (graphicBuffer == 0) { - ST_LOGE("dequeueBuffer: SurfaceComposer::createGraphicBuffer " - "failed"); - return error; - } - if (updateFormat) { - mPixelFormat = format; - } - mSlots[buf].mGraphicBuffer = graphicBuffer; - mSlots[buf].mRequestBufferCalled = false; - mSlots[buf].mFence = EGL_NO_SYNC_KHR; - if (mSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) { - eglDestroyImageKHR(mSlots[buf].mEglDisplay, - mSlots[buf].mEglImage); - mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR; - mSlots[buf].mEglDisplay = EGL_NO_DISPLAY; - } - if (mCurrentTexture == buf) { - // The current texture no longer references the buffer in this slot - // since we just allocated a new buffer. - mCurrentTexture = INVALID_BUFFER_SLOT; - } - returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION; - } - - dpy = mSlots[buf].mEglDisplay; - fence = mSlots[buf].mFence; - mSlots[buf].mFence = EGL_NO_SYNC_KHR; - } - - if (fence != EGL_NO_SYNC_KHR) { - EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000); - // If something goes wrong, log the error, but return the buffer without - // synchronizing access to it. It's too late at this point to abort the - // dequeue operation. - if (result == EGL_FALSE) { - ALOGE("dequeueBuffer: error waiting for fence: %#x", eglGetError()); - } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { - ALOGE("dequeueBuffer: timeout waiting for fence"); - } - eglDestroySyncKHR(dpy, fence); - } - - ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", *outBuf, - mSlots[*outBuf].mGraphicBuffer->handle, returnFlags); - - return returnFlags; -} - -status_t SurfaceTexture::setSynchronousMode(bool enabled) { - ST_LOGV("setSynchronousMode: enabled=%d", enabled); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - ST_LOGE("setSynchronousMode: SurfaceTexture has been abandoned!"); - return NO_INIT; - } - - status_t err = OK; - if (!mAllowSynchronousMode && enabled) - return err; - - if (!enabled) { - // going to asynchronous mode, drain the queue - err = drainQueueLocked(); - if (err != NO_ERROR) - return err; - } - - if (mSynchronousMode != enabled) { - // - if we're going to asynchronous mode, the queue is guaranteed to be - // empty here - // - if the client set the number of buffers, we're guaranteed that - // we have at least 3 (because we don't allow less) - mSynchronousMode = enabled; - mDequeueCondition.signal(); - } - return err; -} - -status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp, - uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) { - ST_LOGV("queueBuffer: slot=%d time=%lld", buf, timestamp); - - sp<FrameAvailableListener> listener; - - { // scope for the lock - Mutex::Autolock lock(mMutex); - if (mAbandoned) { - ST_LOGE("queueBuffer: SurfaceTexture has been abandoned!"); - return NO_INIT; - } - if (buf < 0 || buf >= mBufferCount) { - ST_LOGE("queueBuffer: slot index out of range [0, %d]: %d", - mBufferCount, buf); - return -EINVAL; - } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { - ST_LOGE("queueBuffer: slot %d is not owned by the client " - "(state=%d)", buf, mSlots[buf].mBufferState); - return -EINVAL; - } else if (buf == mCurrentTexture) { - ST_LOGE("queueBuffer: slot %d is current!", buf); - return -EINVAL; - } else if (!mSlots[buf].mRequestBufferCalled) { - ST_LOGE("queueBuffer: slot %d was enqueued without requesting a " - "buffer", buf); - return -EINVAL; - } - - if (mSynchronousMode) { - // In synchronous mode we queue all buffers in a FIFO. - mQueue.push_back(buf); - - // Synchronous mode always signals that an additional frame should - // be consumed. - listener = mFrameAvailableListener; - } else { - // In asynchronous mode we only keep the most recent buffer. - if (mQueue.empty()) { - mQueue.push_back(buf); - - // Asynchronous mode only signals that a frame should be - // consumed if no previous frame was pending. If a frame were - // pending then the consumer would have already been notified. - listener = mFrameAvailableListener; - } else { - Fifo::iterator front(mQueue.begin()); - // buffer currently queued is freed - mSlots[*front].mBufferState = BufferSlot::FREE; - // and we record the new buffer index in the queued list - *front = buf; - } - } - - mSlots[buf].mBufferState = BufferSlot::QUEUED; - mSlots[buf].mCrop = mNextCrop; - mSlots[buf].mTransform = mNextTransform; - mSlots[buf].mScalingMode = mNextScalingMode; - mSlots[buf].mTimestamp = timestamp; - mFrameCounter++; - mSlots[buf].mFrameNumber = mFrameCounter; - - mDequeueCondition.signal(); - - *outWidth = mDefaultWidth; - *outHeight = mDefaultHeight; - *outTransform = 0; - } // scope for the lock - - // call back without lock held - if (listener != 0) { - listener->onFrameAvailable(); - } - return OK; -} - -void SurfaceTexture::cancelBuffer(int buf) { - ST_LOGV("cancelBuffer: slot=%d", buf); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - ST_LOGW("cancelBuffer: SurfaceTexture has been abandoned!"); - return; - } - - if (buf < 0 || buf >= mBufferCount) { - ST_LOGE("cancelBuffer: slot index out of range [0, %d]: %d", - mBufferCount, buf); - return; - } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { - ST_LOGE("cancelBuffer: slot %d is not owned by the client (state=%d)", - buf, mSlots[buf].mBufferState); - return; - } - mSlots[buf].mBufferState = BufferSlot::FREE; - mSlots[buf].mFrameNumber = 0; - mDequeueCondition.signal(); -} - -status_t SurfaceTexture::setCrop(const Rect& crop) { - ST_LOGV("setCrop: crop=[%d,%d,%d,%d]", crop.left, crop.top, crop.right, - crop.bottom); - - Mutex::Autolock lock(mMutex); - if (mAbandoned) { - ST_LOGE("setCrop: SurfaceTexture has been abandoned!"); - return NO_INIT; - } - mNextCrop = crop; - return OK; -} - -status_t SurfaceTexture::setTransform(uint32_t transform) { - ST_LOGV("setTransform: xform=%#x", transform); - Mutex::Autolock lock(mMutex); - if (mAbandoned) { - ST_LOGE("setTransform: SurfaceTexture has been abandoned!"); - return NO_INIT; - } - mNextTransform = transform; - return OK; -} - -status_t SurfaceTexture::connect(int api, - uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) { - ST_LOGV("connect: api=%d", api); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - ST_LOGE("connect: SurfaceTexture has been abandoned!"); - return NO_INIT; - } - - int err = NO_ERROR; - switch (api) { - case NATIVE_WINDOW_API_EGL: - case NATIVE_WINDOW_API_CPU: - case NATIVE_WINDOW_API_MEDIA: - case NATIVE_WINDOW_API_CAMERA: - if (mConnectedApi != NO_CONNECTED_API) { - ST_LOGE("connect: already connected (cur=%d, req=%d)", - mConnectedApi, api); - err = -EINVAL; - } else { - mConnectedApi = api; - *outWidth = mDefaultWidth; - *outHeight = mDefaultHeight; - *outTransform = 0; - } - break; - default: - err = -EINVAL; - break; - } - return err; -} - -status_t SurfaceTexture::disconnect(int api) { - ST_LOGV("disconnect: api=%d", api); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - // it is not really an error to disconnect after the surface - // has been abandoned, it should just be a no-op. - return NO_ERROR; - } - - int err = NO_ERROR; - switch (api) { - case NATIVE_WINDOW_API_EGL: - case NATIVE_WINDOW_API_CPU: - case NATIVE_WINDOW_API_MEDIA: - case NATIVE_WINDOW_API_CAMERA: - if (mConnectedApi == api) { - drainQueueAndFreeBuffersLocked(); - mConnectedApi = NO_CONNECTED_API; - mNextCrop.makeInvalid(); - mNextScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE; - mNextTransform = 0; - mDequeueCondition.signal(); - } else { - ST_LOGE("disconnect: connected to another api (cur=%d, req=%d)", - mConnectedApi, api); - err = -EINVAL; - } - break; - default: - ST_LOGE("disconnect: unknown API %d", api); - err = -EINVAL; - break; - } - return err; -} - -status_t SurfaceTexture::setScalingMode(int mode) { - ST_LOGV("setScalingMode: mode=%d", mode); - - switch (mode) { - case NATIVE_WINDOW_SCALING_MODE_FREEZE: - case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: - break; - default: - ST_LOGE("unknown scaling mode: %d", mode); - return BAD_VALUE; - } - - Mutex::Autolock lock(mMutex); - mNextScalingMode = mode; - return OK; -} - status_t SurfaceTexture::updateTexImage() { ST_LOGV("updateTexImage"); Mutex::Autolock lock(mMutex); @@ -980,69 +375,6 @@ void SurfaceTexture::setFrameAvailableListener( mFrameAvailableListener = listener; } -void SurfaceTexture::freeBufferLocked(int i) { - mSlots[i].mGraphicBuffer = 0; - mSlots[i].mBufferState = BufferSlot::FREE; - mSlots[i].mFrameNumber = 0; - if (mSlots[i].mEglImage != EGL_NO_IMAGE_KHR) { - eglDestroyImageKHR(mSlots[i].mEglDisplay, mSlots[i].mEglImage); - mSlots[i].mEglImage = EGL_NO_IMAGE_KHR; - mSlots[i].mEglDisplay = EGL_NO_DISPLAY; - } -} - -void SurfaceTexture::freeAllBuffersLocked() { - ALOGW_IF(!mQueue.isEmpty(), - "freeAllBuffersLocked called but mQueue is not empty"); - mCurrentTexture = INVALID_BUFFER_SLOT; - for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { - freeBufferLocked(i); - } -} - -void SurfaceTexture::freeAllBuffersExceptHeadLocked() { - ALOGW_IF(!mQueue.isEmpty(), - "freeAllBuffersExceptCurrentLocked called but mQueue is not empty"); - int head = -1; - if (!mQueue.empty()) { - Fifo::iterator front(mQueue.begin()); - head = *front; - } - mCurrentTexture = INVALID_BUFFER_SLOT; - for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { - if (i != head) { - freeBufferLocked(i); - } - } -} - -status_t SurfaceTexture::drainQueueLocked() { - while (mSynchronousMode && !mQueue.isEmpty()) { - mDequeueCondition.wait(mMutex); - if (mAbandoned) { - ST_LOGE("drainQueueLocked: SurfaceTexture has been abandoned!"); - return NO_INIT; - } - if (mConnectedApi == NO_CONNECTED_API) { - ST_LOGE("drainQueueLocked: SurfaceTexture is not connected!"); - return NO_INIT; - } - } - return NO_ERROR; -} - -status_t SurfaceTexture::drainQueueAndFreeBuffersLocked() { - status_t err = drainQueueLocked(); - if (err == NO_ERROR) { - if (mSynchronousMode) { - freeAllBuffersLocked(); - } else { - freeAllBuffersExceptHeadLocked(); - } - } - return err; -} - EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy, const sp<GraphicBuffer>& graphicBuffer) { EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer(); diff --git a/libs/ui/InputTransport.cpp b/libs/ui/InputTransport.cpp index 09cbb318cd..ecb3fb5c98 100644 --- a/libs/ui/InputTransport.cpp +++ b/libs/ui/InputTransport.cpp @@ -7,325 +7,203 @@ //#define LOG_NDEBUG 0 -// Log debug messages about channel signalling (send signal, receive signal) -#define DEBUG_CHANNEL_SIGNALS 0 +// Log debug messages about channel messages (send message, receive message) +#define DEBUG_CHANNEL_MESSAGES 0 // Log debug messages whenever InputChannel objects are created/destroyed #define DEBUG_CHANNEL_LIFECYCLE 0 -// Log debug messages about transport actions (initialize, reset, publish, ...) +// Log debug messages about transport actions #define DEBUG_TRANSPORT_ACTIONS 0 -#include <cutils/ashmem.h> #include <cutils/log.h> #include <errno.h> #include <fcntl.h> -#include <sys/mman.h> #include <ui/InputTransport.h> #include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> -namespace android { - -#define ROUND_UP(value, boundary) (((value) + (boundary) - 1) & ~((boundary) - 1)) -#define MIN_HISTORY_DEPTH 20 -// Must be at least sizeof(InputMessage) + sufficient space for pointer data -static const int DEFAULT_MESSAGE_BUFFER_SIZE = ROUND_UP( - sizeof(InputMessage) + MIN_HISTORY_DEPTH - * (sizeof(InputMessage::SampleData) + MAX_POINTERS * sizeof(PointerCoords)), - 4096); - -// Signal sent by the producer to the consumer to inform it that a new message is -// available to be consumed in the shared memory buffer. -static const char INPUT_SIGNAL_DISPATCH = 'D'; +namespace android { -// Signal sent by the consumer to the producer to inform it that it has finished -// consuming the most recent message and it handled it. -static const char INPUT_SIGNAL_FINISHED_HANDLED = 'f'; +// Socket buffer size. The default is typically about 128KB, which is much larger than +// we really need. So we make it smaller. It just needs to be big enough to hold +// a few dozen large multi-finger motion events in the case where an application gets +// behind processing touches. +static const size_t SOCKET_BUFFER_SIZE = 32 * 1024; + + +// --- InputMessage --- + +bool InputMessage::isValid(size_t actualSize) const { + if (size() == actualSize) { + switch (header.type) { + case TYPE_KEY: + return true; + case TYPE_MOTION: + return body.motion.pointerCount > 0 + && body.motion.pointerCount <= MAX_POINTERS; + case TYPE_FINISHED: + return true; + } + } + return false; +} -// Signal sent by the consumer to the producer to inform it that it has finished -// consuming the most recent message but it did not handle it. -static const char INPUT_SIGNAL_FINISHED_UNHANDLED = 'u'; +size_t InputMessage::size() const { + switch (header.type) { + case TYPE_KEY: + return sizeof(Header) + body.key.size(); + case TYPE_MOTION: + return sizeof(Header) + body.motion.size(); + case TYPE_FINISHED: + return sizeof(Header) + body.finished.size(); + } + return sizeof(Header); +} // --- InputChannel --- -InputChannel::InputChannel(const String8& name, int32_t ashmemFd, int32_t receivePipeFd, - int32_t sendPipeFd) : - mName(name), mAshmemFd(ashmemFd), mReceivePipeFd(receivePipeFd), mSendPipeFd(sendPipeFd) { +InputChannel::InputChannel(const String8& name, int fd) : + mName(name), mFd(fd) { #if DEBUG_CHANNEL_LIFECYCLE - ALOGD("Input channel constructed: name='%s', ashmemFd=%d, receivePipeFd=%d, sendPipeFd=%d", - mName.string(), ashmemFd, receivePipeFd, sendPipeFd); + ALOGD("Input channel constructed: name='%s', fd=%d", + mName.string(), fd); #endif - int result = fcntl(mReceivePipeFd, F_SETFL, O_NONBLOCK); - LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make receive pipe " - "non-blocking. errno=%d", mName.string(), errno); - - result = fcntl(mSendPipeFd, F_SETFL, O_NONBLOCK); - LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make send pipe " + int result = fcntl(mFd, F_SETFL, O_NONBLOCK); + LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make socket " "non-blocking. errno=%d", mName.string(), errno); } InputChannel::~InputChannel() { #if DEBUG_CHANNEL_LIFECYCLE - ALOGD("Input channel destroyed: name='%s', ashmemFd=%d, receivePipeFd=%d, sendPipeFd=%d", - mName.string(), mAshmemFd, mReceivePipeFd, mSendPipeFd); + ALOGD("Input channel destroyed: name='%s', fd=%d", + mName.string(), mFd); #endif - ::close(mAshmemFd); - ::close(mReceivePipeFd); - ::close(mSendPipeFd); + ::close(mFd); } status_t InputChannel::openInputChannelPair(const String8& name, sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) { - status_t result; - - String8 ashmemName("InputChannel "); - ashmemName.append(name); - int serverAshmemFd = ashmem_create_region(ashmemName.string(), DEFAULT_MESSAGE_BUFFER_SIZE); - if (serverAshmemFd < 0) { - result = -errno; - ALOGE("channel '%s' ~ Could not create shared memory region. errno=%d", + int sockets[2]; + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) { + status_t result = -errno; + ALOGE("channel '%s' ~ Could not create socket pair. errno=%d", name.string(), errno); - } else { - result = ashmem_set_prot_region(serverAshmemFd, PROT_READ | PROT_WRITE); - if (result < 0) { - ALOGE("channel '%s' ~ Error %d trying to set protection of ashmem fd %d.", - name.string(), result, serverAshmemFd); - } else { - // Dup the file descriptor because the server and client input channel objects that - // are returned may have different lifetimes but they share the same shared memory region. - int clientAshmemFd; - clientAshmemFd = dup(serverAshmemFd); - if (clientAshmemFd < 0) { - result = -errno; - ALOGE("channel '%s' ~ Could not dup() shared memory region fd. errno=%d", - name.string(), errno); - } else { - int forward[2]; - if (pipe(forward)) { - result = -errno; - ALOGE("channel '%s' ~ Could not create forward pipe. errno=%d", - name.string(), errno); - } else { - int reverse[2]; - if (pipe(reverse)) { - result = -errno; - ALOGE("channel '%s' ~ Could not create reverse pipe. errno=%d", - name.string(), errno); - } else { - String8 serverChannelName = name; - serverChannelName.append(" (server)"); - outServerChannel = new InputChannel(serverChannelName, - serverAshmemFd, reverse[0], forward[1]); - - String8 clientChannelName = name; - clientChannelName.append(" (client)"); - outClientChannel = new InputChannel(clientChannelName, - clientAshmemFd, forward[0], reverse[1]); - return OK; - } - ::close(forward[0]); - ::close(forward[1]); - } - ::close(clientAshmemFd); - } - } - ::close(serverAshmemFd); + outServerChannel.clear(); + outClientChannel.clear(); + return result; } - outServerChannel.clear(); - outClientChannel.clear(); - return result; + int bufferSize = SOCKET_BUFFER_SIZE; + setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)); + setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)); + setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)); + setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)); + + String8 serverChannelName = name; + serverChannelName.append(" (server)"); + outServerChannel = new InputChannel(serverChannelName, sockets[0]); + + String8 clientChannelName = name; + clientChannelName.append(" (client)"); + outClientChannel = new InputChannel(clientChannelName, sockets[1]); + return OK; } -status_t InputChannel::sendSignal(char signal) { +status_t InputChannel::sendMessage(const InputMessage* msg) { + size_t msgLength = msg->size(); ssize_t nWrite; do { - nWrite = ::write(mSendPipeFd, & signal, 1); + nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); } while (nWrite == -1 && errno == EINTR); - if (nWrite == 1) { -#if DEBUG_CHANNEL_SIGNALS - ALOGD("channel '%s' ~ sent signal '%c'", mName.string(), signal); + if (nWrite < 0) { + int error = errno; +#if DEBUG_CHANNEL_MESSAGES + ALOGD("channel '%s' ~ error sending message of type %d, errno=%d", mName.string(), + msg->header.type, error); #endif - return OK; + if (error == EAGAIN || error == EWOULDBLOCK) { + return WOULD_BLOCK; + } + if (error == EPIPE || error == ENOTCONN) { + return DEAD_OBJECT; + } + return -error; } -#if DEBUG_CHANNEL_SIGNALS - ALOGD("channel '%s' ~ error sending signal '%c', errno=%d", mName.string(), signal, errno); + if (size_t(nWrite) != msgLength) { +#if DEBUG_CHANNEL_MESSAGES + ALOGD("channel '%s' ~ error sending message type %d, send was incomplete", + mName.string(), msg->header.type); #endif - return -errno; + return DEAD_OBJECT; + } + +#if DEBUG_CHANNEL_MESSAGES + ALOGD("channel '%s' ~ sent message of type %d", mName.string(), msg->header.type); +#endif + return OK; } -status_t InputChannel::receiveSignal(char* outSignal) { +status_t InputChannel::receiveMessage(InputMessage* msg) { ssize_t nRead; do { - nRead = ::read(mReceivePipeFd, outSignal, 1); + nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT); } while (nRead == -1 && errno == EINTR); - if (nRead == 1) { -#if DEBUG_CHANNEL_SIGNALS - ALOGD("channel '%s' ~ received signal '%c'", mName.string(), *outSignal); + if (nRead < 0) { + int error = errno; +#if DEBUG_CHANNEL_MESSAGES + ALOGD("channel '%s' ~ receive message failed, errno=%d", mName.string(), errno); #endif - return OK; + if (error == EAGAIN || error == EWOULDBLOCK) { + return WOULD_BLOCK; + } + if (error == EPIPE || error == ENOTCONN) { + return DEAD_OBJECT; + } + return -error; } if (nRead == 0) { // check for EOF -#if DEBUG_CHANNEL_SIGNALS - ALOGD("channel '%s' ~ receive signal failed because peer was closed", mName.string()); +#if DEBUG_CHANNEL_MESSAGES + ALOGD("channel '%s' ~ receive message failed because peer was closed", mName.string()); #endif return DEAD_OBJECT; } - if (errno == EAGAIN) { -#if DEBUG_CHANNEL_SIGNALS - ALOGD("channel '%s' ~ receive signal failed because no signal available", mName.string()); + if (!msg->isValid(nRead)) { +#if DEBUG_CHANNEL_MESSAGES + ALOGD("channel '%s' ~ received invalid message", mName.string()); #endif - return WOULD_BLOCK; + return BAD_VALUE; } -#if DEBUG_CHANNEL_SIGNALS - ALOGD("channel '%s' ~ receive signal failed, errno=%d", mName.string(), errno); +#if DEBUG_CHANNEL_MESSAGES + ALOGD("channel '%s' ~ received message of type %d", mName.string(), msg->header.type); #endif - return -errno; + return OK; } // --- InputPublisher --- InputPublisher::InputPublisher(const sp<InputChannel>& channel) : - mChannel(channel), mSharedMessage(NULL), - mPinned(false), mSemaphoreInitialized(false), mWasDispatched(false), - mMotionEventSampleDataTail(NULL) { + mChannel(channel) { } InputPublisher::~InputPublisher() { - reset(); - - if (mSharedMessage) { - munmap(mSharedMessage, mAshmemSize); - } -} - -status_t InputPublisher::initialize() { -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' publisher ~ initialize", - mChannel->getName().string()); -#endif - - int ashmemFd = mChannel->getAshmemFd(); - int result = ashmem_get_size_region(ashmemFd); - if (result < 0) { - ALOGE("channel '%s' publisher ~ Error %d getting size of ashmem fd %d.", - mChannel->getName().string(), result, ashmemFd); - return UNKNOWN_ERROR; - } - mAshmemSize = (size_t) result; - - mSharedMessage = static_cast<InputMessage*>(mmap(NULL, mAshmemSize, - PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0)); - if (! mSharedMessage) { - ALOGE("channel '%s' publisher ~ mmap failed on ashmem fd %d.", - mChannel->getName().string(), ashmemFd); - return NO_MEMORY; - } - - mPinned = true; - mSharedMessage->consumed = false; - - return reset(); -} - -status_t InputPublisher::reset() { -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' publisher ~ reset", - mChannel->getName().string()); -#endif - - if (mPinned) { - // Destroy the semaphore since we are about to unpin the memory region that contains it. - int result; - if (mSemaphoreInitialized) { - if (mSharedMessage->consumed) { - result = sem_post(& mSharedMessage->semaphore); - if (result < 0) { - ALOGE("channel '%s' publisher ~ Error %d in sem_post.", - mChannel->getName().string(), errno); - return UNKNOWN_ERROR; - } - } - - result = sem_destroy(& mSharedMessage->semaphore); - if (result < 0) { - ALOGE("channel '%s' publisher ~ Error %d in sem_destroy.", - mChannel->getName().string(), errno); - return UNKNOWN_ERROR; - } - - mSemaphoreInitialized = false; - } - - // Unpin the region since we no longer care about its contents. - int ashmemFd = mChannel->getAshmemFd(); - result = ashmem_unpin_region(ashmemFd, 0, 0); - if (result < 0) { - ALOGE("channel '%s' publisher ~ Error %d unpinning ashmem fd %d.", - mChannel->getName().string(), result, ashmemFd); - return UNKNOWN_ERROR; - } - - mPinned = false; - } - - mMotionEventSampleDataTail = NULL; - mWasDispatched = false; - return OK; -} - -status_t InputPublisher::publishInputEvent( - int32_t type, - int32_t deviceId, - int32_t source) { - if (mPinned) { - ALOGE("channel '%s' publisher ~ Attempted to publish a new event but publisher has " - "not yet been reset.", mChannel->getName().string()); - return INVALID_OPERATION; - } - - // Pin the region. - // We do not check for ASHMEM_NOT_PURGED because we don't care about the previous - // contents of the buffer so it does not matter whether it was purged in the meantime. - int ashmemFd = mChannel->getAshmemFd(); - int result = ashmem_pin_region(ashmemFd, 0, 0); - if (result < 0) { - ALOGE("channel '%s' publisher ~ Error %d pinning ashmem fd %d.", - mChannel->getName().string(), result, ashmemFd); - return UNKNOWN_ERROR; - } - - mPinned = true; - - result = sem_init(& mSharedMessage->semaphore, 1, 1); - if (result < 0) { - ALOGE("channel '%s' publisher ~ Error %d in sem_init.", - mChannel->getName().string(), errno); - return UNKNOWN_ERROR; - } - - mSemaphoreInitialized = true; - - mSharedMessage->consumed = false; - mSharedMessage->type = type; - mSharedMessage->deviceId = deviceId; - mSharedMessage->source = source; - return OK; } status_t InputPublisher::publishKeyEvent( + uint32_t seq, int32_t deviceId, int32_t source, int32_t action, @@ -337,31 +215,37 @@ status_t InputPublisher::publishKeyEvent( nsecs_t downTime, nsecs_t eventTime) { #if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' publisher ~ publishKeyEvent: deviceId=%d, source=0x%x, " + ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, " "action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d," "downTime=%lld, eventTime=%lld", - mChannel->getName().string(), + mChannel->getName().string(), seq, deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount, downTime, eventTime); #endif - status_t result = publishInputEvent(AINPUT_EVENT_TYPE_KEY, deviceId, source); - if (result < 0) { - return result; + if (!seq) { + ALOGE("Attempted to publish a key event with sequence number 0."); + return BAD_VALUE; } - mSharedMessage->key.action = action; - mSharedMessage->key.flags = flags; - mSharedMessage->key.keyCode = keyCode; - mSharedMessage->key.scanCode = scanCode; - mSharedMessage->key.metaState = metaState; - mSharedMessage->key.repeatCount = repeatCount; - mSharedMessage->key.downTime = downTime; - mSharedMessage->key.eventTime = eventTime; - return OK; + InputMessage msg; + msg.header.type = InputMessage::TYPE_KEY; + msg.body.key.seq = seq; + msg.body.key.deviceId = deviceId; + msg.body.key.source = source; + msg.body.key.action = action; + msg.body.key.flags = flags; + msg.body.key.keyCode = keyCode; + msg.body.key.scanCode = scanCode; + msg.body.key.metaState = metaState; + msg.body.key.repeatCount = repeatCount; + msg.body.key.downTime = downTime; + msg.body.key.eventTime = eventTime; + return mChannel->sendMessage(&msg); } status_t InputPublisher::publishMotionEvent( + uint32_t seq, int32_t deviceId, int32_t source, int32_t action, @@ -379,349 +263,349 @@ status_t InputPublisher::publishMotionEvent( const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { #if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' publisher ~ publishMotionEvent: deviceId=%d, source=0x%x, " + ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, " "action=0x%x, flags=0x%x, edgeFlags=0x%x, metaState=0x%x, buttonState=0x%x, " "xOffset=%f, yOffset=%f, " "xPrecision=%f, yPrecision=%f, downTime=%lld, eventTime=%lld, " "pointerCount=%d", - mChannel->getName().string(), + mChannel->getName().string(), seq, deviceId, source, action, flags, edgeFlags, metaState, buttonState, xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount); #endif + if (!seq) { + ALOGE("Attempted to publish a motion event with sequence number 0."); + return BAD_VALUE; + } + if (pointerCount > MAX_POINTERS || pointerCount < 1) { ALOGE("channel '%s' publisher ~ Invalid number of pointers provided: %d.", mChannel->getName().string(), pointerCount); return BAD_VALUE; } - status_t result = publishInputEvent(AINPUT_EVENT_TYPE_MOTION, deviceId, source); - if (result < 0) { - return result; - } - - mSharedMessage->motion.action = action; - mSharedMessage->motion.flags = flags; - mSharedMessage->motion.edgeFlags = edgeFlags; - mSharedMessage->motion.metaState = metaState; - mSharedMessage->motion.buttonState = buttonState; - mSharedMessage->motion.xOffset = xOffset; - mSharedMessage->motion.yOffset = yOffset; - mSharedMessage->motion.xPrecision = xPrecision; - mSharedMessage->motion.yPrecision = yPrecision; - mSharedMessage->motion.downTime = downTime; - mSharedMessage->motion.pointerCount = pointerCount; - - mSharedMessage->motion.sampleCount = 1; - mSharedMessage->motion.sampleData[0].eventTime = eventTime; - + InputMessage msg; + msg.header.type = InputMessage::TYPE_MOTION; + msg.body.motion.seq = seq; + msg.body.motion.deviceId = deviceId; + msg.body.motion.source = source; + msg.body.motion.action = action; + msg.body.motion.flags = flags; + msg.body.motion.edgeFlags = edgeFlags; + msg.body.motion.metaState = metaState; + msg.body.motion.buttonState = buttonState; + msg.body.motion.xOffset = xOffset; + msg.body.motion.yOffset = yOffset; + msg.body.motion.xPrecision = xPrecision; + msg.body.motion.yPrecision = yPrecision; + msg.body.motion.downTime = downTime; + msg.body.motion.eventTime = eventTime; + msg.body.motion.pointerCount = pointerCount; for (size_t i = 0; i < pointerCount; i++) { - mSharedMessage->motion.pointerProperties[i].copyFrom(pointerProperties[i]); - mSharedMessage->motion.sampleData[0].coords[i].copyFrom(pointerCoords[i]); - } - - // Cache essential information about the motion event to ensure that a malicious consumer - // cannot confuse the publisher by modifying the contents of the shared memory buffer while - // it is being updated. - if (action == AMOTION_EVENT_ACTION_MOVE - || action == AMOTION_EVENT_ACTION_HOVER_MOVE) { - mMotionEventPointerCount = pointerCount; - mMotionEventSampleDataStride = InputMessage::sampleDataStride(pointerCount); - mMotionEventSampleDataTail = InputMessage::sampleDataPtrIncrement( - mSharedMessage->motion.sampleData, mMotionEventSampleDataStride); - } else { - mMotionEventSampleDataTail = NULL; - } - return OK; -} - -status_t InputPublisher::appendMotionSample( - nsecs_t eventTime, - const PointerCoords* pointerCoords) { -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' publisher ~ appendMotionSample: eventTime=%lld", - mChannel->getName().string(), eventTime); -#endif - - if (! mPinned || ! mMotionEventSampleDataTail) { - ALOGE("channel '%s' publisher ~ Cannot append motion sample because there is no current " - "AMOTION_EVENT_ACTION_MOVE or AMOTION_EVENT_ACTION_HOVER_MOVE event.", - mChannel->getName().string()); - return INVALID_OPERATION; - } - - InputMessage::SampleData* newTail = InputMessage::sampleDataPtrIncrement( - mMotionEventSampleDataTail, mMotionEventSampleDataStride); - size_t newBytesUsed = reinterpret_cast<char*>(newTail) - - reinterpret_cast<char*>(mSharedMessage); - - if (newBytesUsed > mAshmemSize) { -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' publisher ~ Cannot append motion sample because the shared memory " - "buffer is full. Buffer size: %d bytes, pointers: %d, samples: %d", - mChannel->getName().string(), - mAshmemSize, mMotionEventPointerCount, mSharedMessage->motion.sampleCount); -#endif - return NO_MEMORY; + msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]); + msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]); } - - int result; - if (mWasDispatched) { - result = sem_trywait(& mSharedMessage->semaphore); - if (result < 0) { - if (errno == EAGAIN) { - // Only possible source of contention is the consumer having consumed (or being in the - // process of consuming) the message and left the semaphore count at 0. -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' publisher ~ Cannot append motion sample because the message has " - "already been consumed.", mChannel->getName().string()); -#endif - return FAILED_TRANSACTION; - } else { - ALOGE("channel '%s' publisher ~ Error %d in sem_trywait.", - mChannel->getName().string(), errno); - return UNKNOWN_ERROR; - } - } - } - - mMotionEventSampleDataTail->eventTime = eventTime; - for (size_t i = 0; i < mMotionEventPointerCount; i++) { - mMotionEventSampleDataTail->coords[i].copyFrom(pointerCoords[i]); - } - mMotionEventSampleDataTail = newTail; - - mSharedMessage->motion.sampleCount += 1; - - if (mWasDispatched) { - result = sem_post(& mSharedMessage->semaphore); - if (result < 0) { - ALOGE("channel '%s' publisher ~ Error %d in sem_post.", - mChannel->getName().string(), errno); - return UNKNOWN_ERROR; - } - } - return OK; -} - -status_t InputPublisher::sendDispatchSignal() { -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' publisher ~ sendDispatchSignal", - mChannel->getName().string()); -#endif - - mWasDispatched = true; - return mChannel->sendSignal(INPUT_SIGNAL_DISPATCH); + return mChannel->sendMessage(&msg); } -status_t InputPublisher::receiveFinishedSignal(bool* outHandled) { +status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) { #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' publisher ~ receiveFinishedSignal", mChannel->getName().string()); #endif - char signal; - status_t result = mChannel->receiveSignal(& signal); + InputMessage msg; + status_t result = mChannel->receiveMessage(&msg); if (result) { + *outSeq = 0; *outHandled = false; return result; } - if (signal == INPUT_SIGNAL_FINISHED_HANDLED) { - *outHandled = true; - } else if (signal == INPUT_SIGNAL_FINISHED_UNHANDLED) { - *outHandled = false; - } else { - ALOGE("channel '%s' publisher ~ Received unexpected signal '%c' from consumer", - mChannel->getName().string(), signal); + if (msg.header.type != InputMessage::TYPE_FINISHED) { + ALOGE("channel '%s' publisher ~ Received unexpected message of type %d from consumer", + mChannel->getName().string(), msg.header.type); return UNKNOWN_ERROR; } + *outSeq = msg.body.finished.seq; + *outHandled = msg.body.finished.handled; return OK; } // --- InputConsumer --- InputConsumer::InputConsumer(const sp<InputChannel>& channel) : - mChannel(channel), mSharedMessage(NULL) { + mChannel(channel), mMsgDeferred(false) { } InputConsumer::~InputConsumer() { - if (mSharedMessage) { - munmap(mSharedMessage, mAshmemSize); - } } -status_t InputConsumer::initialize() { +status_t InputConsumer::consume(InputEventFactoryInterface* factory, + bool consumeBatches, uint32_t* outSeq, InputEvent** outEvent) { #if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' consumer ~ initialize", - mChannel->getName().string()); + ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s", + mChannel->getName().string(), consumeBatches ? "true" : "false"); #endif - int ashmemFd = mChannel->getAshmemFd(); - int result = ashmem_get_size_region(ashmemFd); - if (result < 0) { - ALOGE("channel '%s' consumer ~ Error %d getting size of ashmem fd %d.", - mChannel->getName().string(), result, ashmemFd); - return UNKNOWN_ERROR; - } + *outSeq = 0; + *outEvent = NULL; - mAshmemSize = (size_t) result; + // Fetch the next input message. + // Loop until an event can be returned or no additional events are received. + while (!*outEvent) { + if (mMsgDeferred) { + // mMsg contains a valid input message from the previous call to consume + // that has not yet been processed. + mMsgDeferred = false; + } else { + // Receive a fresh message. + status_t result = mChannel->receiveMessage(&mMsg); + if (result) { + // Consume the next batched event unless batches are being held for later. + if (!mBatches.isEmpty() && (consumeBatches || result != WOULD_BLOCK)) { + MotionEvent* motionEvent = factory->createMotionEvent(); + if (! motionEvent) return NO_MEMORY; + + const Batch& batch = mBatches.top(); + motionEvent->copyFrom(&batch.event, true /*keepHistory*/); + *outSeq = batch.seq; + *outEvent = motionEvent; + mBatches.pop(); +#if DEBUG_TRANSPORT_ACTIONS + ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u", + mChannel->getName().string(), *outSeq); +#endif + break; + } + return result; + } + } - mSharedMessage = static_cast<InputMessage*>(mmap(NULL, mAshmemSize, - PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0)); - if (! mSharedMessage) { - ALOGE("channel '%s' consumer ~ mmap failed on ashmem fd %d.", - mChannel->getName().string(), ashmemFd); - return NO_MEMORY; - } + switch (mMsg.header.type) { + case InputMessage::TYPE_KEY: { + KeyEvent* keyEvent = factory->createKeyEvent(); + if (!keyEvent) return NO_MEMORY; - return OK; -} + initializeKeyEvent(keyEvent, &mMsg); + *outSeq = mMsg.body.key.seq; + *outEvent = keyEvent; +#if DEBUG_TRANSPORT_ACTIONS + ALOGD("channel '%s' consumer ~ consumed key event, seq=%u", + mChannel->getName().string(), *outSeq); +#endif + break; + } -status_t InputConsumer::consume(InputEventFactoryInterface* factory, InputEvent** outEvent) { + case AINPUT_EVENT_TYPE_MOTION: { + ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source); + if (batchIndex >= 0) { + Batch& batch = mBatches.editItemAt(batchIndex); + if (canAppendSamples(&batch.event, &mMsg)) { + // Append to the batch and save the new sequence number for the tail end. + uint32_t chain = batch.seq; + appendSamples(&batch.event, &mMsg); + batch.seq = mMsg.body.motion.seq; + + // Update the sequence number chain. + SeqChain seqChain; + seqChain.seq = batch.seq; + seqChain.chain = chain; + mSeqChains.push(seqChain); #if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' consumer ~ consume", - mChannel->getName().string()); + ALOGD("channel '%s' consumer ~ appended to batch event", + mChannel->getName().string()); #endif + break; + } else { + MotionEvent* motionEvent = factory->createMotionEvent(); + if (! motionEvent) return NO_MEMORY; + + // We cannot append to the batch in progress, so we need to consume + // the previous batch right now and defer the new message until later. + mMsgDeferred = true; + + // Return the end of the previous batch. + motionEvent->copyFrom(&batch.event, true /*keepHistory*/); + *outSeq = batch.seq; + *outEvent = motionEvent; + mBatches.removeAt(batchIndex); +#if DEBUG_TRANSPORT_ACTIONS + ALOGD("channel '%s' consumer ~ consumed batch event and " + "deferred current event, seq=%u", + mChannel->getName().string(), *outSeq); +#endif + break; + } + } - *outEvent = NULL; + // Start a new batch if needed. + if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE + || mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) { + mBatches.push(); + Batch& batch = mBatches.editTop(); + batch.seq = mMsg.body.motion.seq; + initializeMotionEvent(&batch.event, &mMsg); +#if DEBUG_TRANSPORT_ACTIONS + ALOGD("channel '%s' consumer ~ started batch event", + mChannel->getName().string()); +#endif + break; + } + + MotionEvent* motionEvent = factory->createMotionEvent(); + if (! motionEvent) return NO_MEMORY; - int ashmemFd = mChannel->getAshmemFd(); - int result = ashmem_pin_region(ashmemFd, 0, 0); - if (result != ASHMEM_NOT_PURGED) { - if (result == ASHMEM_WAS_PURGED) { - ALOGE("channel '%s' consumer ~ Error %d pinning ashmem fd %d because it was purged " - "which probably indicates that the publisher and consumer are out of sync.", - mChannel->getName().string(), result, ashmemFd); - return INVALID_OPERATION; + initializeMotionEvent(motionEvent, &mMsg); + *outSeq = mMsg.body.motion.seq; + *outEvent = motionEvent; +#if DEBUG_TRANSPORT_ACTIONS + ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u", + mChannel->getName().string(), *outSeq); +#endif + break; } - ALOGE("channel '%s' consumer ~ Error %d pinning ashmem fd %d.", - mChannel->getName().string(), result, ashmemFd); - return UNKNOWN_ERROR; + default: + ALOGE("channel '%s' consumer ~ Received unexpected message of type %d", + mChannel->getName().string(), mMsg.header.type); + return UNKNOWN_ERROR; + } } + return OK; +} - if (mSharedMessage->consumed) { - ALOGE("channel '%s' consumer ~ The current message has already been consumed.", - mChannel->getName().string()); - return INVALID_OPERATION; - } +status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) { +#if DEBUG_TRANSPORT_ACTIONS + ALOGD("channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s", + mChannel->getName().string(), seq, handled ? "true" : "false"); +#endif - // Acquire but *never release* the semaphore. Contention on the semaphore is used to signal - // to the publisher that the message has been consumed (or is in the process of being - // consumed). Eventually the publisher will reinitialize the semaphore for the next message. - result = sem_wait(& mSharedMessage->semaphore); - if (result < 0) { - ALOGE("channel '%s' consumer ~ Error %d in sem_wait.", - mChannel->getName().string(), errno); - return UNKNOWN_ERROR; + if (!seq) { + ALOGE("Attempted to send a finished signal with sequence number 0."); + return BAD_VALUE; } - mSharedMessage->consumed = true; - - switch (mSharedMessage->type) { - case AINPUT_EVENT_TYPE_KEY: { - KeyEvent* keyEvent = factory->createKeyEvent(); - if (! keyEvent) return NO_MEMORY; - - populateKeyEvent(keyEvent); - - *outEvent = keyEvent; - break; + // Send finished signals for the batch sequence chain first. + size_t seqChainCount = mSeqChains.size(); + if (seqChainCount) { + uint32_t currentSeq = seq; + uint32_t chainSeqs[seqChainCount]; + size_t chainIndex = 0; + for (size_t i = seqChainCount; i-- > 0; ) { + const SeqChain& seqChain = mSeqChains.itemAt(i); + if (seqChain.seq == currentSeq) { + currentSeq = seqChain.chain; + chainSeqs[chainIndex++] = currentSeq; + mSeqChains.removeAt(i); + } + } + status_t status = OK; + while (!status && chainIndex-- > 0) { + status = sendUnchainedFinishedSignal(chainSeqs[chainIndex], handled); + } + if (status) { + // An error occurred so at least one signal was not sent, reconstruct the chain. + do { + SeqChain seqChain; + seqChain.seq = chainIndex != 0 ? chainSeqs[chainIndex - 1] : seq; + seqChain.chain = chainSeqs[chainIndex]; + mSeqChains.push(seqChain); + } while (chainIndex-- > 0); + return status; + } } - case AINPUT_EVENT_TYPE_MOTION: { - MotionEvent* motionEvent = factory->createMotionEvent(); - if (! motionEvent) return NO_MEMORY; + // Send finished signal for the last message in the batch. + return sendUnchainedFinishedSignal(seq, handled); +} - populateMotionEvent(motionEvent); +status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) { + InputMessage msg; + msg.header.type = InputMessage::TYPE_FINISHED; + msg.body.finished.seq = seq; + msg.body.finished.handled = handled; + return mChannel->sendMessage(&msg); +} - *outEvent = motionEvent; - break; - } +bool InputConsumer::hasPendingBatch() const { + return !mBatches.isEmpty(); +} - default: - ALOGE("channel '%s' consumer ~ Received message of unknown type %d", - mChannel->getName().string(), mSharedMessage->type); - return UNKNOWN_ERROR; +ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const { + for (size_t i = 0; i < mBatches.size(); i++) { + const Batch& batch = mBatches.itemAt(i); + if (batch.event.getDeviceId() == deviceId && batch.event.getSource() == source) { + return i; + } } - - return OK; + return -1; } -status_t InputConsumer::sendFinishedSignal(bool handled) { -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' consumer ~ sendFinishedSignal: handled=%d", - mChannel->getName().string(), handled); -#endif - - return mChannel->sendSignal(handled - ? INPUT_SIGNAL_FINISHED_HANDLED - : INPUT_SIGNAL_FINISHED_UNHANDLED); +void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) { + event->initialize( + msg->body.key.deviceId, + msg->body.key.source, + msg->body.key.action, + msg->body.key.flags, + msg->body.key.keyCode, + msg->body.key.scanCode, + msg->body.key.metaState, + msg->body.key.repeatCount, + msg->body.key.downTime, + msg->body.key.eventTime); } -status_t InputConsumer::receiveDispatchSignal() { -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' consumer ~ receiveDispatchSignal", - mChannel->getName().string()); -#endif +void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) { + size_t pointerCount = msg->body.motion.pointerCount; + PointerProperties pointerProperties[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + for (size_t i = 0; i < pointerCount; i++) { + pointerProperties[i].copyFrom(msg->body.motion.pointers[i].properties); + pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords); + } + + event->initialize( + msg->body.motion.deviceId, + msg->body.motion.source, + msg->body.motion.action, + msg->body.motion.flags, + msg->body.motion.edgeFlags, + msg->body.motion.metaState, + msg->body.motion.buttonState, + msg->body.motion.xOffset, + msg->body.motion.yOffset, + msg->body.motion.xPrecision, + msg->body.motion.yPrecision, + msg->body.motion.downTime, + msg->body.motion.eventTime, + pointerCount, + pointerProperties, + pointerCoords); +} - char signal; - status_t result = mChannel->receiveSignal(& signal); - if (result) { - return result; +bool InputConsumer::canAppendSamples(const MotionEvent* event, const InputMessage *msg) { + size_t pointerCount = msg->body.motion.pointerCount; + if (event->getPointerCount() != pointerCount + || event->getAction() != msg->body.motion.action) { + return false; } - if (signal != INPUT_SIGNAL_DISPATCH) { - ALOGE("channel '%s' consumer ~ Received unexpected signal '%c' from publisher", - mChannel->getName().string(), signal); - return UNKNOWN_ERROR; + for (size_t i = 0; i < pointerCount; i++) { + if (*event->getPointerProperties(i) != msg->body.motion.pointers[i].properties) { + return false; + } } - return OK; -} - -void InputConsumer::populateKeyEvent(KeyEvent* keyEvent) const { - keyEvent->initialize( - mSharedMessage->deviceId, - mSharedMessage->source, - mSharedMessage->key.action, - mSharedMessage->key.flags, - mSharedMessage->key.keyCode, - mSharedMessage->key.scanCode, - mSharedMessage->key.metaState, - mSharedMessage->key.repeatCount, - mSharedMessage->key.downTime, - mSharedMessage->key.eventTime); + return true; } -void InputConsumer::populateMotionEvent(MotionEvent* motionEvent) const { - motionEvent->initialize( - mSharedMessage->deviceId, - mSharedMessage->source, - mSharedMessage->motion.action, - mSharedMessage->motion.flags, - mSharedMessage->motion.edgeFlags, - mSharedMessage->motion.metaState, - mSharedMessage->motion.buttonState, - mSharedMessage->motion.xOffset, - mSharedMessage->motion.yOffset, - mSharedMessage->motion.xPrecision, - mSharedMessage->motion.yPrecision, - mSharedMessage->motion.downTime, - mSharedMessage->motion.sampleData[0].eventTime, - mSharedMessage->motion.pointerCount, - mSharedMessage->motion.pointerProperties, - mSharedMessage->motion.sampleData[0].coords); - - size_t sampleCount = mSharedMessage->motion.sampleCount; - if (sampleCount > 1) { - InputMessage::SampleData* sampleData = mSharedMessage->motion.sampleData; - size_t sampleDataStride = InputMessage::sampleDataStride( - mSharedMessage->motion.pointerCount); - - while (--sampleCount > 0) { - sampleData = InputMessage::sampleDataPtrIncrement(sampleData, sampleDataStride); - motionEvent->addSample(sampleData->eventTime, sampleData->coords); - } +void InputConsumer::appendSamples(MotionEvent* event, const InputMessage* msg) { + size_t pointerCount = msg->body.motion.pointerCount; + PointerCoords pointerCoords[pointerCount]; + for (size_t i = 0; i < pointerCount; i++) { + pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords); } + + event->setMetaState(event->getMetaState() | msg->body.motion.metaState); + event->addSample(msg->body.motion.eventTime, pointerCoords); } } // namespace android diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp index 8cd047afb8..6e2e731e3d 100644 --- a/libs/ui/Region.cpp +++ b/libs/ui/Region.cpp @@ -126,6 +126,9 @@ void Region::addRectUnchecked(int l, int t, int r, int b) Region& Region::orSelf(const Rect& r) { return operationSelf(r, op_or); } +Region& Region::xorSelf(const Rect& r) { + return operationSelf(r, op_xor); +} Region& Region::andSelf(const Rect& r) { return operationSelf(r, op_and); } @@ -143,6 +146,9 @@ Region& Region::operationSelf(const Rect& r, int op) { Region& Region::orSelf(const Region& rhs) { return operationSelf(rhs, op_or); } +Region& Region::xorSelf(const Region& rhs) { + return operationSelf(rhs, op_xor); +} Region& Region::andSelf(const Region& rhs) { return operationSelf(rhs, op_and); } @@ -165,6 +171,9 @@ Region& Region::translateSelf(int x, int y) { const Region Region::merge(const Rect& rhs) const { return operation(rhs, op_or); } +const Region Region::mergeExclusive(const Rect& rhs) const { + return operation(rhs, op_xor); +} const Region Region::intersect(const Rect& rhs) const { return operation(rhs, op_and); } @@ -182,6 +191,9 @@ const Region Region::operation(const Rect& rhs, int op) const { const Region Region::merge(const Region& rhs) const { return operation(rhs, op_or); } +const Region Region::mergeExclusive(const Region& rhs) const { + return operation(rhs, op_xor); +} const Region Region::intersect(const Region& rhs) const { return operation(rhs, op_and); } @@ -205,6 +217,9 @@ const Region Region::translate(int x, int y) const { Region& Region::orSelf(const Region& rhs, int dx, int dy) { return operationSelf(rhs, dx, dy, op_or); } +Region& Region::xorSelf(const Region& rhs, int dx, int dy) { + return operationSelf(rhs, dx, dy, op_xor); +} Region& Region::andSelf(const Region& rhs, int dx, int dy) { return operationSelf(rhs, dx, dy, op_and); } @@ -222,6 +237,9 @@ Region& Region::operationSelf(const Region& rhs, int dx, int dy, int op) { const Region Region::merge(const Region& rhs, int dx, int dy) const { return operation(rhs, dx, dy, op_or); } +const Region Region::mergeExclusive(const Region& rhs, int dx, int dy) const { + return operation(rhs, dx, dy, op_xor); +} const Region Region::intersect(const Region& rhs, int dx, int dy) const { return operation(rhs, dx, dy, op_and); } @@ -421,6 +439,7 @@ void Region::boolean_operation(int op, Region& dst, SkRegion::Op sk_op; switch (op) { case op_or: sk_op = SkRegion::kUnion_Op; name="OR"; break; + case op_xor: sk_op = SkRegion::kUnion_XOR; name="XOR"; break; case op_and: sk_op = SkRegion::kIntersect_Op; name="AND"; break; case op_nand: sk_op = SkRegion::kDifference_Op; name="NAND"; break; } diff --git a/libs/ui/tests/InputChannel_test.cpp b/libs/ui/tests/InputChannel_test.cpp index eff22ee5f0..ee422fe7d8 100644 --- a/libs/ui/tests/InputChannel_test.cpp +++ b/libs/ui/tests/InputChannel_test.cpp @@ -20,8 +20,7 @@ #include <gtest/gtest.h> #include <unistd.h> #include <time.h> -#include <sys/mman.h> -#include <cutils/ashmem.h> +#include <errno.h> #include "../../utils/tests/TestHelpers.h" @@ -36,35 +35,24 @@ protected: TEST_F(InputChannelTest, ConstructorAndDestructor_TakesOwnershipOfFileDescriptors) { // Our purpose here is to verify that the input channel destructor closes the - // file descriptors provided to it. One easy way is to provide it with one end + // file descriptor provided to it. One easy way is to provide it with one end // of a pipe and to check for EPIPE on the other end after the channel is destroyed. - Pipe fakeAshmem, sendPipe, receivePipe; + Pipe pipe; - sp<InputChannel> inputChannel = new InputChannel(String8("channel name"), - fakeAshmem.sendFd, receivePipe.receiveFd, sendPipe.sendFd); + sp<InputChannel> inputChannel = new InputChannel(String8("channel name"), pipe.sendFd); EXPECT_STREQ("channel name", inputChannel->getName().string()) << "channel should have provided name"; - EXPECT_EQ(fakeAshmem.sendFd, inputChannel->getAshmemFd()) - << "channel should have provided ashmem fd"; - EXPECT_EQ(receivePipe.receiveFd, inputChannel->getReceivePipeFd()) - << "channel should have provided receive pipe fd"; - EXPECT_EQ(sendPipe.sendFd, inputChannel->getSendPipeFd()) - << "channel should have provided send pipe fd"; + EXPECT_EQ(pipe.sendFd, inputChannel->getFd()) + << "channel should have provided fd"; inputChannel.clear(); // destroys input channel - EXPECT_EQ(-EPIPE, fakeAshmem.readSignal()) - << "channel should have closed ashmem fd when destroyed"; - EXPECT_EQ(-EPIPE, receivePipe.writeSignal()) - << "channel should have closed receive pipe fd when destroyed"; - EXPECT_EQ(-EPIPE, sendPipe.readSignal()) - << "channel should have closed send pipe fd when destroyed"; + EXPECT_EQ(-EPIPE, pipe.readSignal()) + << "channel should have closed fd when destroyed"; // clean up fds of Pipe endpoints that were closed so we don't try to close them again - fakeAshmem.sendFd = -1; - receivePipe.receiveFd = -1; - sendPipe.sendFd = -1; + pipe.sendFd = -1; } TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { @@ -82,43 +70,40 @@ TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { EXPECT_STREQ("channel name (client)", clientChannel->getName().string()) << "client channel should have suffixed name"; - // Ashmem uniqueness - EXPECT_NE(serverChannel->getAshmemFd(), clientChannel->getAshmemFd()) - << "server and client channel should have different ashmem fds because it was dup'd"; - - // Ashmem usability - ssize_t serverAshmemSize = ashmem_get_size_region(serverChannel->getAshmemFd()); - ssize_t clientAshmemSize = ashmem_get_size_region(clientChannel->getAshmemFd()); - uint32_t* serverAshmem = static_cast<uint32_t*>(mmap(NULL, serverAshmemSize, - PROT_READ | PROT_WRITE, MAP_SHARED, serverChannel->getAshmemFd(), 0)); - uint32_t* clientAshmem = static_cast<uint32_t*>(mmap(NULL, clientAshmemSize, - PROT_READ | PROT_WRITE, MAP_SHARED, clientChannel->getAshmemFd(), 0)); - ASSERT_TRUE(serverAshmem != NULL) - << "server channel ashmem should be mappable"; - ASSERT_TRUE(clientAshmem != NULL) - << "client channel ashmem should be mappable"; - *serverAshmem = 0xf00dd00d; - EXPECT_EQ(0xf00dd00d, *clientAshmem) - << "ashmem buffer should be shared by client and server"; - munmap(serverAshmem, serverAshmemSize); - munmap(clientAshmem, clientAshmemSize); - // Server->Client communication - EXPECT_EQ(OK, serverChannel->sendSignal('S')) - << "server channel should be able to send signal to client channel"; - char signal; - EXPECT_EQ(OK, clientChannel->receiveSignal(& signal)) - << "client channel should be able to receive signal from server channel"; - EXPECT_EQ('S', signal) - << "client channel should receive the correct signal from server channel"; + InputMessage serverMsg; + memset(&serverMsg, 0, sizeof(InputMessage)); + serverMsg.header.type = InputMessage::TYPE_KEY; + serverMsg.body.key.action = AKEY_EVENT_ACTION_DOWN; + EXPECT_EQ(OK, serverChannel->sendMessage(&serverMsg)) + << "server channel should be able to send message to client channel"; + + InputMessage clientMsg; + EXPECT_EQ(OK, clientChannel->receiveMessage(&clientMsg)) + << "client channel should be able to receive message from server channel"; + EXPECT_EQ(serverMsg.header.type, clientMsg.header.type) + << "client channel should receive the correct message from server channel"; + EXPECT_EQ(serverMsg.body.key.action, clientMsg.body.key.action) + << "client channel should receive the correct message from server channel"; // Client->Server communication - EXPECT_EQ(OK, clientChannel->sendSignal('c')) - << "client channel should be able to send signal to server channel"; - EXPECT_EQ(OK, serverChannel->receiveSignal(& signal)) - << "server channel should be able to receive signal from client channel"; - EXPECT_EQ('c', signal) - << "server channel should receive the correct signal from client channel"; + InputMessage clientReply; + memset(&clientReply, 0, sizeof(InputMessage)); + clientReply.header.type = InputMessage::TYPE_FINISHED; + clientReply.body.finished.seq = 0x11223344; + clientReply.body.finished.handled = true; + EXPECT_EQ(OK, clientChannel->sendMessage(&clientReply)) + << "client channel should be able to send message to server channel"; + + InputMessage serverReply; + EXPECT_EQ(OK, serverChannel->receiveMessage(&serverReply)) + << "server channel should be able to receive message from client channel"; + EXPECT_EQ(clientReply.header.type, serverReply.header.type) + << "server channel should receive the correct message from client channel"; + EXPECT_EQ(clientReply.body.finished.seq, serverReply.body.finished.seq) + << "server channel should receive the correct message from client channel"; + EXPECT_EQ(clientReply.body.finished.handled, serverReply.body.finished.handled) + << "server channel should receive the correct message from client channel"; } TEST_F(InputChannelTest, ReceiveSignal_WhenNoSignalPresent_ReturnsAnError) { @@ -130,9 +115,9 @@ TEST_F(InputChannelTest, ReceiveSignal_WhenNoSignalPresent_ReturnsAnError) { ASSERT_EQ(OK, result) << "should have successfully opened a channel pair"; - char signal; - EXPECT_EQ(WOULD_BLOCK, clientChannel->receiveSignal(& signal)) - << "receiveSignal should have returned WOULD_BLOCK"; + InputMessage msg; + EXPECT_EQ(WOULD_BLOCK, clientChannel->receiveMessage(&msg)) + << "receiveMessage should have returned WOULD_BLOCK"; } TEST_F(InputChannelTest, ReceiveSignal_WhenPeerClosed_ReturnsAnError) { @@ -146,9 +131,9 @@ TEST_F(InputChannelTest, ReceiveSignal_WhenPeerClosed_ReturnsAnError) { serverChannel.clear(); // close server channel - char signal; - EXPECT_EQ(DEAD_OBJECT, clientChannel->receiveSignal(& signal)) - << "receiveSignal should have returned DEAD_OBJECT"; + InputMessage msg; + EXPECT_EQ(DEAD_OBJECT, clientChannel->receiveMessage(&msg)) + << "receiveMessage should have returned DEAD_OBJECT"; } TEST_F(InputChannelTest, SendSignal_WhenPeerClosed_ReturnsAnError) { @@ -162,8 +147,10 @@ TEST_F(InputChannelTest, SendSignal_WhenPeerClosed_ReturnsAnError) { serverChannel.clear(); // close server channel - EXPECT_EQ(DEAD_OBJECT, clientChannel->sendSignal('S')) - << "sendSignal should have returned DEAD_OBJECT"; + InputMessage msg; + msg.header.type = InputMessage::TYPE_KEY; + EXPECT_EQ(DEAD_OBJECT, clientChannel->sendMessage(&msg)) + << "sendMessage should have returned DEAD_OBJECT"; } diff --git a/libs/ui/tests/InputPublisherAndConsumer_test.cpp b/libs/ui/tests/InputPublisherAndConsumer_test.cpp index fcc4cadb71..3303053132 100644 --- a/libs/ui/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/ui/tests/InputPublisherAndConsumer_test.cpp @@ -57,11 +57,8 @@ protected: clientChannel.clear(); } - void Initialize(); void PublishAndConsumeKeyEvent(); - void PublishAndConsumeMotionEvent( - size_t samplesToAppendBeforeDispatch = 0, - size_t samplesToAppendAfterDispatch = 0); + void PublishAndConsumeMotionEvent(); }; TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) { @@ -69,21 +66,10 @@ TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) { EXPECT_EQ(clientChannel.get(), mConsumer->getChannel().get()); } -void InputPublisherAndConsumerTest::Initialize() { - status_t status; - - status = mPublisher->initialize(); - ASSERT_EQ(OK, status) - << "publisher initialize should return OK"; - - status = mConsumer->initialize(); - ASSERT_EQ(OK, status) - << "consumer initialize should return OK"; -} - void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { status_t status; + const uint32_t seq = 15; const int32_t deviceId = 1; const int32_t source = AINPUT_SOURCE_KEYBOARD; const int32_t action = AKEY_EVENT_ACTION_DOWN; @@ -95,21 +81,14 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { const nsecs_t downTime = 3; const nsecs_t eventTime = 4; - status = mPublisher->publishKeyEvent(deviceId, source, action, flags, + status = mPublisher->publishKeyEvent(seq, deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount, downTime, eventTime); ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK"; - status = mPublisher->sendDispatchSignal(); - ASSERT_EQ(OK, status) - << "publisher sendDispatchSignal should return OK"; - - status = mConsumer->receiveDispatchSignal(); - ASSERT_EQ(OK, status) - << "consumer receiveDispatchSignal should return OK"; - + uint32_t consumeSeq; InputEvent* event; - status = mConsumer->consume(& mEventFactory, & event); + status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, &consumeSeq, &event); ASSERT_EQ(OK, status) << "consumer consume should return OK"; @@ -119,6 +98,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { << "consumer should have returned a key event"; KeyEvent* keyEvent = static_cast<KeyEvent*>(event); + EXPECT_EQ(seq, consumeSeq); EXPECT_EQ(deviceId, keyEvent->getDeviceId()); EXPECT_EQ(source, keyEvent->getSource()); EXPECT_EQ(action, keyEvent->getAction()); @@ -130,26 +110,25 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { EXPECT_EQ(downTime, keyEvent->getDownTime()); EXPECT_EQ(eventTime, keyEvent->getEventTime()); - status = mConsumer->sendFinishedSignal(true); + status = mConsumer->sendFinishedSignal(seq, true); ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK"; + uint32_t finishedSeq = 0; bool handled = false; - status = mPublisher->receiveFinishedSignal(&handled); + status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled); ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK"; + ASSERT_EQ(seq, finishedSeq) + << "publisher receiveFinishedSignal should have returned the original sequence number"; ASSERT_TRUE(handled) << "publisher receiveFinishedSignal should have set handled to consumer's reply"; - - status = mPublisher->reset(); - ASSERT_EQ(OK, status) - << "publisher reset should return OK"; } -void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent( - size_t samplesToAppendBeforeDispatch, size_t samplesToAppendAfterDispatch) { +void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { status_t status; + const uint32_t seq = 15; const int32_t deviceId = 1; const int32_t source = AINPUT_SOURCE_TOUCHSCREEN; const int32_t action = AMOTION_EVENT_ACTION_MOVE; @@ -163,67 +142,36 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent( const float yPrecision = 0.5; const nsecs_t downTime = 3; const size_t pointerCount = 3; + const nsecs_t eventTime = 4; PointerProperties pointerProperties[pointerCount]; + PointerCoords pointerCoords[pointerCount]; for (size_t i = 0; i < pointerCount; i++) { pointerProperties[i].clear(); pointerProperties[i].id = (i + 2) % pointerCount; pointerProperties[i].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; - } - Vector<nsecs_t> sampleEventTimes; - Vector<PointerCoords> samplePointerCoords; - - for (size_t i = 0; i <= samplesToAppendAfterDispatch + samplesToAppendBeforeDispatch; i++) { - sampleEventTimes.push(i + 10); - for (size_t j = 0; j < pointerCount; j++) { - samplePointerCoords.push(); - PointerCoords& pc = samplePointerCoords.editTop(); - pc.clear(); - pc.setAxisValue(AMOTION_EVENT_AXIS_X, 100 * i + j); - pc.setAxisValue(AMOTION_EVENT_AXIS_Y, 200 * i + j); - pc.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.5 * i + j); - pc.setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.7 * i + j); - pc.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 1.5 * i + j); - pc.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 1.7 * i + j); - pc.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.5 * i + j); - pc.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.7 * i + j); - pc.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i + j); - } + pointerCoords[i].clear(); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, 100 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, 200 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.5 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.7 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 1.5 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 1.7 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.5 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.7 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i); } - status = mPublisher->publishMotionEvent(deviceId, source, action, flags, edgeFlags, + status = mPublisher->publishMotionEvent(seq, deviceId, source, action, flags, edgeFlags, metaState, buttonState, xOffset, yOffset, xPrecision, yPrecision, - downTime, sampleEventTimes[0], pointerCount, - pointerProperties, samplePointerCoords.array()); + downTime, eventTime, pointerCount, + pointerProperties, pointerCoords); ASSERT_EQ(OK, status) << "publisher publishMotionEvent should return OK"; - for (size_t i = 0; i < samplesToAppendBeforeDispatch; i++) { - size_t sampleIndex = i + 1; - status = mPublisher->appendMotionSample(sampleEventTimes[sampleIndex], - samplePointerCoords.array() + sampleIndex * pointerCount); - ASSERT_EQ(OK, status) - << "publisher appendMotionEvent should return OK"; - } - - status = mPublisher->sendDispatchSignal(); - ASSERT_EQ(OK, status) - << "publisher sendDispatchSignal should return OK"; - - for (size_t i = 0; i < samplesToAppendAfterDispatch; i++) { - size_t sampleIndex = i + 1 + samplesToAppendBeforeDispatch; - status = mPublisher->appendMotionSample(sampleEventTimes[sampleIndex], - samplePointerCoords.array() + sampleIndex * pointerCount); - ASSERT_EQ(OK, status) - << "publisher appendMotionEvent should return OK"; - } - - status = mConsumer->receiveDispatchSignal(); - ASSERT_EQ(OK, status) - << "consumer receiveDispatchSignal should return OK"; - + uint32_t consumeSeq; InputEvent* event; - status = mConsumer->consume(& mEventFactory, & event); + status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, &consumeSeq, &event); ASSERT_EQ(OK, status) << "consumer consume should return OK"; @@ -232,9 +180,8 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent( ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType()) << "consumer should have returned a motion event"; - size_t lastSampleIndex = samplesToAppendBeforeDispatch + samplesToAppendAfterDispatch; - MotionEvent* motionEvent = static_cast<MotionEvent*>(event); + EXPECT_EQ(seq, consumeSeq); EXPECT_EQ(deviceId, motionEvent->getDeviceId()); EXPECT_EQ(source, motionEvent->getSource()); EXPECT_EQ(action, motionEvent->getAction()); @@ -245,150 +192,69 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent( EXPECT_EQ(xPrecision, motionEvent->getXPrecision()); EXPECT_EQ(yPrecision, motionEvent->getYPrecision()); EXPECT_EQ(downTime, motionEvent->getDownTime()); - EXPECT_EQ(sampleEventTimes[lastSampleIndex], motionEvent->getEventTime()); + EXPECT_EQ(eventTime, motionEvent->getEventTime()); EXPECT_EQ(pointerCount, motionEvent->getPointerCount()); - EXPECT_EQ(lastSampleIndex, motionEvent->getHistorySize()); + EXPECT_EQ(0U, motionEvent->getHistorySize()); for (size_t i = 0; i < pointerCount; i++) { SCOPED_TRACE(i); EXPECT_EQ(pointerProperties[i].id, motionEvent->getPointerId(i)); EXPECT_EQ(pointerProperties[i].toolType, motionEvent->getToolType(i)); - } - - for (size_t sampleIndex = 0; sampleIndex < lastSampleIndex; sampleIndex++) { - SCOPED_TRACE(sampleIndex); - EXPECT_EQ(sampleEventTimes[sampleIndex], - motionEvent->getHistoricalEventTime(sampleIndex)); - for (size_t i = 0; i < pointerCount; i++) { - SCOPED_TRACE(i); - size_t offset = sampleIndex * pointerCount + i; - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_X), - motionEvent->getHistoricalRawX(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_Y), - motionEvent->getHistoricalRawY(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_X) + xOffset, - motionEvent->getHistoricalX(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_Y) + yOffset, - motionEvent->getHistoricalY(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), - motionEvent->getHistoricalPressure(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_SIZE), - motionEvent->getHistoricalSize(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), - motionEvent->getHistoricalTouchMajor(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), - motionEvent->getHistoricalTouchMinor(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), - motionEvent->getHistoricalToolMajor(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), - motionEvent->getHistoricalToolMinor(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), - motionEvent->getHistoricalOrientation(i, sampleIndex)); - } - } - SCOPED_TRACE(lastSampleIndex); - EXPECT_EQ(sampleEventTimes[lastSampleIndex], motionEvent->getEventTime()); - for (size_t i = 0; i < pointerCount; i++) { - SCOPED_TRACE(i); - size_t offset = lastSampleIndex * pointerCount + i; - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_X), + EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X), motionEvent->getRawX(i)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_Y), + EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y), motionEvent->getRawY(i)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_X) + xOffset, + EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X) + xOffset, motionEvent->getX(i)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_Y) + yOffset, + EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y) + yOffset, motionEvent->getY(i)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), + EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), motionEvent->getPressure(i)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_SIZE), + EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE), motionEvent->getSize(i)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), + EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), motionEvent->getTouchMajor(i)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), + EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), motionEvent->getTouchMinor(i)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), + EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), motionEvent->getToolMajor(i)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), + EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), motionEvent->getToolMinor(i)); - EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), + EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), motionEvent->getOrientation(i)); } - status = mConsumer->sendFinishedSignal(false); + status = mConsumer->sendFinishedSignal(seq, false); ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK"; + uint32_t finishedSeq = 0; bool handled = true; - status = mPublisher->receiveFinishedSignal(&handled); + status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled); ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK"; + ASSERT_EQ(seq, finishedSeq) + << "publisher receiveFinishedSignal should have returned the original sequence number"; ASSERT_FALSE(handled) << "publisher receiveFinishedSignal should have set handled to consumer's reply"; - - status = mPublisher->reset(); - ASSERT_EQ(OK, status) - << "publisher reset should return OK"; } TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) { - ASSERT_NO_FATAL_FAILURE(Initialize()); ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); } -TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_WhenNotReset_ReturnsError) { - status_t status; - ASSERT_NO_FATAL_FAILURE(Initialize()); - - status = mPublisher->publishKeyEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - ASSERT_EQ(OK, status) - << "publisher publishKeyEvent should return OK first time"; - - status = mPublisher->publishKeyEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - ASSERT_EQ(INVALID_OPERATION, status) - << "publisher publishKeyEvent should return INVALID_OPERATION because " - "the publisher was not reset"; -} - TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_EndToEnd) { - ASSERT_NO_FATAL_FAILURE(Initialize()); ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); } -TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenNotReset_ReturnsError) { - status_t status; - ASSERT_NO_FATAL_FAILURE(Initialize()); - - const size_t pointerCount = 1; - PointerProperties pointerProperties[pointerCount]; - PointerCoords pointerCoords[pointerCount]; - for (size_t i = 0; i < pointerCount; i++) { - pointerProperties[i].clear(); - pointerCoords[i].clear(); - } - - status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - pointerCount, pointerProperties, pointerCoords); - ASSERT_EQ(OK, status) - << "publisher publishMotionEvent should return OK"; - - status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - pointerCount, pointerProperties, pointerCoords); - ASSERT_EQ(INVALID_OPERATION, status) - << "publisher publishMotionEvent should return INVALID_OPERATION because "; - "the publisher was not reset"; -} - TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountLessThan1_ReturnsError) { status_t status; - ASSERT_NO_FATAL_FAILURE(Initialize()); - const size_t pointerCount = 0; PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; - status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; @@ -396,8 +262,6 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountLessTha TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountGreaterThanMax_ReturnsError) { status_t status; - ASSERT_NO_FATAL_FAILURE(Initialize()); - const size_t pointerCount = MAX_POINTERS + 1; PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; @@ -406,14 +270,13 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountGreater pointerCoords[i].clear(); } - status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; } TEST_F(InputPublisherAndConsumerTest, PublishMultipleEvents_EndToEnd) { - ASSERT_NO_FATAL_FAILURE(Initialize()); ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); @@ -421,111 +284,4 @@ TEST_F(InputPublisherAndConsumerTest, PublishMultipleEvents_EndToEnd) { ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); } -TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenCalledBeforeDispatchSignal_AppendsSamples) { - status_t status; - ASSERT_NO_FATAL_FAILURE(Initialize()); - ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent(3, 0)); -} - -TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenCalledAfterDispatchSignalAndNotConsumed_AppendsSamples) { - status_t status; - ASSERT_NO_FATAL_FAILURE(Initialize()); - ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent(0, 4)); -} - -TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenNoMotionEventPublished_ReturnsError) { - status_t status; - ASSERT_NO_FATAL_FAILURE(Initialize()); - - PointerCoords pointerCoords[1]; - status = mPublisher->appendMotionSample(0, pointerCoords); - ASSERT_EQ(INVALID_OPERATION, status) - << "publisher appendMotionSample should return INVALID_OPERATION"; -} - -TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenPublishedMotionEventIsNotAMove_ReturnsError) { - status_t status; - ASSERT_NO_FATAL_FAILURE(Initialize()); - - const size_t pointerCount = MAX_POINTERS; - PointerProperties pointerProperties[pointerCount]; - PointerCoords pointerCoords[pointerCount]; - for (size_t i = 0; i < pointerCount; i++) { - pointerProperties[i].clear(); - pointerCoords[i].clear(); - } - - status = mPublisher->publishMotionEvent(0, 0, AMOTION_EVENT_ACTION_DOWN, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords); - ASSERT_EQ(OK, status); - - status = mPublisher->appendMotionSample(0, pointerCoords); - ASSERT_EQ(INVALID_OPERATION, status) - << "publisher appendMotionSample should return INVALID_OPERATION"; -} - -TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenAlreadyConsumed_ReturnsError) { - status_t status; - ASSERT_NO_FATAL_FAILURE(Initialize()); - - const size_t pointerCount = MAX_POINTERS; - PointerProperties pointerProperties[pointerCount]; - PointerCoords pointerCoords[pointerCount]; - for (size_t i = 0; i < pointerCount; i++) { - pointerProperties[i].clear(); - pointerCoords[i].clear(); - } - - status = mPublisher->publishMotionEvent(0, 0, AMOTION_EVENT_ACTION_MOVE, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords); - ASSERT_EQ(OK, status); - - status = mPublisher->sendDispatchSignal(); - ASSERT_EQ(OK, status); - - status = mConsumer->receiveDispatchSignal(); - ASSERT_EQ(OK, status); - - InputEvent* event; - status = mConsumer->consume(& mEventFactory, & event); - ASSERT_EQ(OK, status); - - status = mPublisher->appendMotionSample(0, pointerCoords); - ASSERT_EQ(status_t(FAILED_TRANSACTION), status) - << "publisher appendMotionSample should return FAILED_TRANSACTION"; -} - -TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenBufferFull_ReturnsError) { - status_t status; - ASSERT_NO_FATAL_FAILURE(Initialize()); - - const size_t pointerCount = MAX_POINTERS; - PointerProperties pointerProperties[pointerCount]; - PointerCoords pointerCoords[pointerCount]; - for (size_t i = 0; i < pointerCount; i++) { - pointerProperties[i].clear(); - pointerCoords[i].clear(); - } - - status = mPublisher->publishMotionEvent(0, 0, AMOTION_EVENT_ACTION_MOVE, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords); - ASSERT_EQ(OK, status); - - for (int count = 1;; count++) { - ASSERT_LT(count, 100000) << "should eventually reach OOM"; - - status = mPublisher->appendMotionSample(0, pointerCoords); - if (status != OK) { - ASSERT_GT(count, 12) << "should be able to add at least a dozen samples"; - ASSERT_EQ(NO_MEMORY, status) - << "publisher appendMotionSample should return NO_MEMORY when buffer is full"; - break; - } - } - - status = mPublisher->appendMotionSample(0, pointerCoords); - ASSERT_EQ(NO_MEMORY, status) - << "publisher appendMotionSample should return NO_MEMORY persistently until reset"; -} - } // namespace android diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 544ab744e3..24cf5048fa 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -98,7 +98,8 @@ endif LOCAL_C_INCLUDES += \ external/zlib \ - external/icu4c/common + external/icu4c/common \ + bionic/libc/private LOCAL_LDLIBS += -lpthread @@ -114,7 +115,10 @@ include $(BUILD_SHARED_LIBRARY) ifeq ($(TARGET_OS),linux) include $(CLEAR_VARS) -LOCAL_C_INCLUDES += external/zlib external/icu4c/common +LOCAL_C_INCLUDES += \ + external/zlib \ + external/icu4c/common \ + bionic/libc/private LOCAL_LDLIBS := -lrt -ldl -lpthread LOCAL_MODULE := libutils LOCAL_SRC_FILES := $(commonSources) BackupData.cpp BackupHelpers.cpp diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index e343c62352..ab207f5655 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -34,6 +34,9 @@ # include <pthread.h> # include <sched.h> # include <sys/resource.h> +#ifdef HAVE_ANDROID_OS +# include <bionic_pthread.h> +#endif #elif defined(HAVE_WIN32_THREADS) # include <windows.h> # include <stdint.h> @@ -86,7 +89,7 @@ struct thread_data_t { char * threadName; // we use this trampoline when we need to set the priority with - // nice/setpriority. + // nice/setpriority, and name with prctl. static int trampoline(const thread_data_t* t) { thread_func_t f = t->entryFunction; void* u = t->userData; @@ -141,8 +144,13 @@ int androidCreateRawThreadEtc(android_thread_func_t entryFunction, #ifdef HAVE_ANDROID_OS /* valgrind is rejecting RT-priority create reqs */ if (threadPriority != PRIORITY_DEFAULT || threadName != NULL) { - // We could avoid the trampoline if there was a way to get to the - // android_thread_id_t (pid) from pthread_t + // Now that the pthread_t has a method to find the associated + // android_thread_id_t (pid) from pthread_t, it would be possible to avoid + // this trampoline in some cases as the parent could set the properties + // for the child. However, there would be a race condition because the + // child becomes ready immediately, and it doesn't work for the name. + // prctl(PR_SET_NAME) only works for self; prctl(PR_SET_THREAD_NAME) was + // proposed but not yet accepted. thread_data_t* t = new thread_data_t; t->priority = threadPriority; t->threadName = threadName ? strdup(threadName) : NULL; @@ -178,6 +186,13 @@ int androidCreateRawThreadEtc(android_thread_func_t entryFunction, return 1; } +#ifdef HAVE_ANDROID_OS +static pthread_t android_thread_id_t_to_pthread(android_thread_id_t thread) +{ + return (pthread_t) thread; +} +#endif + android_thread_id_t androidGetThreadId() { return (android_thread_id_t)pthread_self(); @@ -909,6 +924,23 @@ status_t Thread::join() return mStatus; } +#ifdef HAVE_ANDROID_OS +pid_t Thread::getTid() const +{ + // mTid is not defined until the child initializes it, and the caller may need it earlier + Mutex::Autolock _l(mLock); + pid_t tid; + if (mRunning) { + pthread_t pthread = android_thread_id_t_to_pthread(mThread); + tid = __pthread_gettid(pthread); + } else { + ALOGW("Thread (this=%p): getTid() is undefined before run()", this); + tid = -1; + } + return tid; +} +#endif + bool Thread::exitPending() const { Mutex::Autolock _l(mLock); diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp index 06be2ef114..0b1016c23f 100644 --- a/opengl/libs/EGL/Loader.cpp +++ b/opengl/libs/EGL/Loader.cpp @@ -118,12 +118,6 @@ status_t Loader::driver_t::set(void* hnd, int32_t api) // ---------------------------------------------------------------------------- -Loader::entry_t::entry_t(int dpy, int impl, const char* tag) - : dpy(dpy), impl(impl), tag(tag) { -} - -// ---------------------------------------------------------------------------- - Loader::Loader() { char line[256]; @@ -131,8 +125,9 @@ Loader::Loader() /* Special case for GLES emulation */ if (checkGlesEmulationStatus() == 0) { - ALOGD("Emulator without GPU support detected. Fallback to software renderer."); - gConfig.add( entry_t(0, 0, "android") ); + ALOGD("Emulator without GPU support detected. " + "Fallback to software renderer."); + mDriverTag.setTo("android"); return; } @@ -141,14 +136,16 @@ Loader::Loader() if (cfg == NULL) { // default config ALOGD("egl.cfg not found, using default config"); - gConfig.add( entry_t(0, 0, "android") ); + mDriverTag.setTo("android"); } else { while (fgets(line, 256, cfg)) { - int dpy; - int impl; + int dpy, impl; if (sscanf(line, "%u %u %s", &dpy, &impl, tag) == 3) { //ALOGD(">>> %u %u %s", dpy, impl, tag); - gConfig.add( entry_t(dpy, impl, tag) ); + // We only load the h/w accelerated implementation + if (tag != String8("android")) { + mDriverTag = tag; + } } } fclose(cfg); @@ -160,30 +157,12 @@ Loader::~Loader() GLTrace_stop(); } -const char* Loader::getTag(int dpy, int impl) +void* Loader::open(egl_connection_t* cnx) { - const Vector<entry_t>& cfgs(gConfig); - const size_t c = cfgs.size(); - for (size_t i=0 ; i<c ; i++) { - if (dpy == cfgs[i].dpy) - if (impl == cfgs[i].impl) - return cfgs[i].tag.string(); - } - return 0; -} - -void* Loader::open(EGLNativeDisplayType display, int impl, egl_connection_t* cnx) -{ - /* - * TODO: if we don't find display/0, then use 0/0 - * (0/0 should always work) - */ - void* dso; - int index = int(display); driver_t* hnd = 0; - char const* tag = getTag(index, impl); + char const* tag = mDriverTag.string(); if (tag) { dso = load_driver("GLES", tag, cnx, EGL | GLESv1_CM | GLESv2); if (dso) { @@ -193,16 +172,14 @@ void* Loader::open(EGLNativeDisplayType display, int impl, egl_connection_t* cnx dso = load_driver("EGL", tag, cnx, EGL); if (dso) { hnd = new driver_t(dso); - // TODO: make this more automated hnd->set( load_driver("GLESv1_CM", tag, cnx, GLESv1_CM), GLESv1_CM ); - - hnd->set( load_driver("GLESv2", tag, cnx, GLESv2), GLESv2 ); + hnd->set( load_driver("GLESv2", tag, cnx, GLESv2), GLESv2 ); } } } - LOG_FATAL_IF(!index && !impl && !hnd, + LOG_FATAL_IF(!index && !hnd, "couldn't find the default OpenGL ES implementation " "for default display"); @@ -221,7 +198,7 @@ void Loader::init_api(void* dso, __eglMustCastToProperFunctionPointerType* curr, getProcAddressType getProcAddress) { - const size_t SIZE = 256; + const ssize_t SIZE = 256; char scrap[SIZE]; while (*api) { char const * name = *api; @@ -326,14 +303,14 @@ void *Loader::load_driver(const char* kind, const char *tag, if (mask & GLESv1_CM) { init_api(dso, gl_names, (__eglMustCastToProperFunctionPointerType*) - &cnx->hooks[GLESv1_INDEX]->gl, + &cnx->hooks[egl_connection_t::GLESv1_INDEX]->gl, getProcAddress); } if (mask & GLESv2) { init_api(dso, gl_names, (__eglMustCastToProperFunctionPointerType*) - &cnx->hooks[GLESv2_INDEX]->gl, + &cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl, getProcAddress); } diff --git a/opengl/libs/EGL/Loader.h b/opengl/libs/EGL/Loader.h index 580d6e4c39..30773cb6db 100644 --- a/opengl/libs/EGL/Loader.h +++ b/opengl/libs/EGL/Loader.h @@ -24,7 +24,6 @@ #include <utils/Errors.h> #include <utils/Singleton.h> #include <utils/String8.h> -#include <utils/Vector.h> #include <EGL/egl.h> @@ -53,23 +52,13 @@ class Loader : public Singleton<Loader> void* dso[3]; }; - struct entry_t { - entry_t() { } - entry_t(int dpy, int impl, const char* tag); - int dpy; - int impl; - String8 tag; - }; - - Vector<entry_t> gConfig; + String8 mDriverTag; getProcAddressType getProcAddress; - const char* getTag(int dpy, int impl); - public: ~Loader(); - void* open(EGLNativeDisplayType display, int impl, egl_connection_t* cnx); + void* open(egl_connection_t* cnx); status_t close(void* driver); private: diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp index 83933e5252..4a56dcffde 100644 --- a/opengl/libs/EGL/egl.cpp +++ b/opengl/libs/EGL/egl.cpp @@ -48,8 +48,8 @@ namespace android { // ---------------------------------------------------------------------------- -egl_connection_t gEGLImpl[IMPL_NUM_IMPLEMENTATIONS]; -gl_hooks_t gHooks[2][IMPL_NUM_IMPLEMENTATIONS]; +egl_connection_t gEGLImpl; +gl_hooks_t gHooks[2]; gl_hooks_t gHooksNoContext; pthread_key_t gGLWrapperKey = -1; @@ -187,16 +187,13 @@ egl_display_t* validate_display(EGLDisplay dpy) { return dp; } -egl_connection_t* validate_display_config(EGLDisplay dpy, EGLConfig config, +egl_connection_t* validate_display_config(EGLDisplay dpy, EGLConfig, egl_display_t const*& dp) { dp = validate_display(dpy); if (!dp) return (egl_connection_t*) NULL; - if (intptr_t(config) >= dp->numTotalConfigs) { - return setError(EGL_BAD_CONFIG, (egl_connection_t*)NULL); - } - egl_connection_t* const cnx = &gEGLImpl[dp->configs[intptr_t(config)].impl]; + egl_connection_t* const cnx = &gEGLImpl; if (cnx->dso == 0) { return setError(EGL_BAD_CONFIG, (egl_connection_t*)NULL); } @@ -228,7 +225,7 @@ EGLImageKHR egl_get_image_for_current_context(EGLImageKHR image) // EGL. egl_image_t const * const i = get_image(image); - return i->images[c->impl]; + return i->image; } // ---------------------------------------------------------------------------- @@ -266,34 +263,17 @@ static EGLBoolean egl_init_drivers_locked() { // get our driver loader Loader& loader(Loader::getInstance()); - // dynamically load all our EGL implementations - egl_connection_t* cnx; - - cnx = &gEGLImpl[IMPL_SOFTWARE]; - if (cnx->dso == 0) { - cnx->hooks[GLESv1_INDEX] = &gHooks[GLESv1_INDEX][IMPL_SOFTWARE]; - cnx->hooks[GLESv2_INDEX] = &gHooks[GLESv2_INDEX][IMPL_SOFTWARE]; - cnx->dso = loader.open(EGL_DEFAULT_DISPLAY, 0, cnx); - } - - cnx = &gEGLImpl[IMPL_HARDWARE]; + // dynamically load our EGL implementation + egl_connection_t* cnx = &gEGLImpl; if (cnx->dso == 0) { - char value[PROPERTY_VALUE_MAX]; - property_get("debug.egl.hw", value, "1"); - if (atoi(value) != 0) { - cnx->hooks[GLESv1_INDEX] = &gHooks[GLESv1_INDEX][IMPL_HARDWARE]; - cnx->hooks[GLESv2_INDEX] = &gHooks[GLESv2_INDEX][IMPL_HARDWARE]; - cnx->dso = loader.open(EGL_DEFAULT_DISPLAY, 1, cnx); - } else { - ALOGD("3D hardware acceleration is disabled"); - } - } - - if (!gEGLImpl[IMPL_SOFTWARE].dso && !gEGLImpl[IMPL_HARDWARE].dso) { - return EGL_FALSE; + cnx->hooks[egl_connection_t::GLESv1_INDEX] = + &gHooks[egl_connection_t::GLESv1_INDEX]; + cnx->hooks[egl_connection_t::GLESv2_INDEX] = + &gHooks[egl_connection_t::GLESv2_INDEX]; + cnx->dso = loader.open(cnx); } - return EGL_TRUE; + return cnx->dso ? EGL_TRUE : EGL_FALSE; } static pthread_mutex_t sInitDriverMutex = PTHREAD_MUTEX_INITIALIZER; diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp index 73aab26cc7..bb2783dc26 100644 --- a/opengl/libs/EGL/eglApi.cpp +++ b/opengl/libs/EGL/eglApi.cpp @@ -44,6 +44,7 @@ #include "egl_impl.h" #include "egl_object.h" #include "egl_tls.h" +#include "egldefs.h" using namespace android; @@ -88,24 +89,6 @@ static void(*findProcAddress(const char* name, // ---------------------------------------------------------------------------- -template<typename T> -static __attribute__((noinline)) -int binarySearch(T const sortedArray[], int first, int last, T key) { - while (first <= last) { - int mid = (first + last) / 2; - if (sortedArray[mid] < key) { - first = mid + 1; - } else if (key < sortedArray[mid]) { - last = mid - 1; - } else { - return mid; - } - } - return -1; -} - -// ---------------------------------------------------------------------------- - namespace android { extern void setGLHooksThreadSpecific(gl_hooks_t const *value); extern EGLBoolean egl_init_drivers(); @@ -183,21 +166,20 @@ EGLBoolean eglGetConfigs( EGLDisplay dpy, egl_display_t const * const dp = validate_display(dpy); if (!dp) return EGL_FALSE; - GLint numConfigs = dp->numTotalConfigs; - if (!configs) { - *num_config = numConfigs; - return EGL_TRUE; + if (num_config==0) { + return setError(EGL_BAD_PARAMETER, EGL_FALSE); } - GLint n = 0; - for (intptr_t i=0 ; i<dp->numTotalConfigs && config_size ; i++) { - *configs++ = EGLConfig(i); - config_size--; - n++; + EGLBoolean res = EGL_FALSE; + *num_config = 0; + + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso) { + res = cnx->egl.eglGetConfigs( + dp->disp.dpy, configs, config_size, num_config); } - - *num_config = n; - return EGL_TRUE; + + return res; } EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list, @@ -213,105 +195,13 @@ EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list, return setError(EGL_BAD_PARAMETER, EGL_FALSE); } - EGLint n; EGLBoolean res = EGL_FALSE; *num_config = 0; - - // It is unfortunate, but we need to remap the EGL_CONFIG_IDs, - // to do this, we have to go through the attrib_list array once - // to figure out both its size and if it contains an EGL_CONFIG_ID - // key. If so, the full array is copied and patched. - // NOTE: we assume that there can be only one occurrence - // of EGL_CONFIG_ID. - - EGLint patch_index = -1; - GLint attr; - size_t size = 0; - if (attrib_list) { - while ((attr=attrib_list[size]) != EGL_NONE) { - if (attr == EGL_CONFIG_ID) - patch_index = size; - size += 2; - } - } - if (patch_index >= 0) { - size += 2; // we need copy the sentinel as well - EGLint* new_list = (EGLint*)malloc(size*sizeof(EGLint)); - if (new_list == 0) - return setError(EGL_BAD_ALLOC, EGL_FALSE); - memcpy(new_list, attrib_list, size*sizeof(EGLint)); - - // patch the requested EGL_CONFIG_ID - bool found = false; - EGLConfig ourConfig(0); - EGLint& configId(new_list[patch_index+1]); - for (intptr_t i=0 ; i<dp->numTotalConfigs ; i++) { - if (dp->configs[i].configId == configId) { - ourConfig = EGLConfig(i); - configId = dp->configs[i].implConfigId; - found = true; - break; - } - } - - egl_connection_t* const cnx = &gEGLImpl[dp->configs[intptr_t(ourConfig)].impl]; - if (found && cnx->dso) { - // and switch to the new list - attrib_list = const_cast<const EGLint *>(new_list); - - // At this point, the only configuration that can match is - // dp->configs[i][index], however, we don't know if it would be - // rejected because of the other attributes, so we do have to call - // cnx->egl.eglChooseConfig() -- but we don't have to loop - // through all the EGLimpl[]. - // We also know we can only get a single config back, and we know - // which one. - - res = cnx->egl.eglChooseConfig( - dp->disp[ dp->configs[intptr_t(ourConfig)].impl ].dpy, - attrib_list, configs, config_size, &n); - if (res && n>0) { - // n has to be 0 or 1, by construction, and we already know - // which config it will return (since there can be only one). - if (configs) { - configs[0] = ourConfig; - } - *num_config = 1; - } - } - - free(const_cast<EGLint *>(attrib_list)); - return res; - } - - - for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) { - egl_connection_t* const cnx = &gEGLImpl[i]; - if (cnx->dso) { - if (cnx->egl.eglChooseConfig( - dp->disp[i].dpy, attrib_list, configs, config_size, &n)) { - if (configs) { - // now we need to convert these client EGLConfig to our - // internal EGLConfig format. - // This is done in O(n Log(n)) time. - for (int j=0 ; j<n ; j++) { - egl_config_t key(i, configs[j]); - intptr_t index = binarySearch<egl_config_t>( - dp->configs, 0, dp->numTotalConfigs, key); - if (index >= 0) { - configs[j] = EGLConfig(index); - } else { - return setError(EGL_BAD_CONFIG, EGL_FALSE); - } - } - configs += n; - config_size -= n; - } - *num_config += n; - res = EGL_TRUE; - } - } + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso) { + res = cnx->egl.eglChooseConfig( + dp->disp.dpy, attrib_list, configs, config_size, num_config); } return res; } @@ -325,13 +215,8 @@ EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, egl_connection_t* cnx = validate_display_config(dpy, config, dp); if (!cnx) return EGL_FALSE; - if (attribute == EGL_CONFIG_ID) { - *value = dp->configs[intptr_t(config)].configId; - return EGL_TRUE; - } return cnx->egl.eglGetConfigAttrib( - dp->disp[ dp->configs[intptr_t(config)].impl ].dpy, - dp->configs[intptr_t(config)].config, attribute, value); + dp->disp.dpy, config, attribute, value); } // ---------------------------------------------------------------------------- @@ -347,8 +232,7 @@ EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config, egl_display_t const* dp = 0; egl_connection_t* cnx = validate_display_config(dpy, config, dp); if (cnx) { - EGLDisplay iDpy = dp->disp[ dp->configs[intptr_t(config)].impl ].dpy; - EGLConfig iConfig = dp->configs[intptr_t(config)].config; + EGLDisplay iDpy = dp->disp.dpy; EGLint format; if (native_window_api_connect(window, NATIVE_WINDOW_API_EGL) != OK) { @@ -359,7 +243,7 @@ EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config, // set the native window's buffers format to match this config if (cnx->egl.eglGetConfigAttrib(iDpy, - iConfig, EGL_NATIVE_VISUAL_ID, &format)) { + config, EGL_NATIVE_VISUAL_ID, &format)) { if (format != 0) { int err = native_window_set_buffers_format(window, format); if (err != 0) { @@ -377,10 +261,9 @@ EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config, anw->setSwapInterval(anw, 1); EGLSurface surface = cnx->egl.eglCreateWindowSurface( - iDpy, iConfig, window, attrib_list); + iDpy, config, window, attrib_list); if (surface != EGL_NO_SURFACE) { - egl_surface_t* s = new egl_surface_t(dpy, config, window, surface, - dp->configs[intptr_t(config)].impl, cnx); + egl_surface_t* s = new egl_surface_t(dpy, config, window, surface, cnx); return s; } @@ -401,11 +284,9 @@ EGLSurface eglCreatePixmapSurface( EGLDisplay dpy, EGLConfig config, egl_connection_t* cnx = validate_display_config(dpy, config, dp); if (cnx) { EGLSurface surface = cnx->egl.eglCreatePixmapSurface( - dp->disp[ dp->configs[intptr_t(config)].impl ].dpy, - dp->configs[intptr_t(config)].config, pixmap, attrib_list); + dp->disp.dpy, config, pixmap, attrib_list); if (surface != EGL_NO_SURFACE) { - egl_surface_t* s = new egl_surface_t(dpy, config, NULL, surface, - dp->configs[intptr_t(config)].impl, cnx); + egl_surface_t* s = new egl_surface_t(dpy, config, NULL, surface, cnx); return s; } } @@ -421,11 +302,9 @@ EGLSurface eglCreatePbufferSurface( EGLDisplay dpy, EGLConfig config, egl_connection_t* cnx = validate_display_config(dpy, config, dp); if (cnx) { EGLSurface surface = cnx->egl.eglCreatePbufferSurface( - dp->disp[ dp->configs[intptr_t(config)].impl ].dpy, - dp->configs[intptr_t(config)].config, attrib_list); + dp->disp.dpy, config, attrib_list); if (surface != EGL_NO_SURFACE) { - egl_surface_t* s = new egl_surface_t(dpy, config, NULL, surface, - dp->configs[intptr_t(config)].impl, cnx); + egl_surface_t* s = new egl_surface_t(dpy, config, NULL, surface, cnx); return s; } } @@ -444,8 +323,7 @@ EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface) return setError(EGL_BAD_SURFACE, EGL_FALSE); egl_surface_t * const s = get_surface(surface); - EGLBoolean result = s->cnx->egl.eglDestroySurface( - dp->disp[s->impl].dpy, s->surface); + EGLBoolean result = s->cnx->egl.eglDestroySurface(dp->disp.dpy, s->surface); if (result == EGL_TRUE) { _s.terminate(); } @@ -465,16 +343,8 @@ EGLBoolean eglQuerySurface( EGLDisplay dpy, EGLSurface surface, return setError(EGL_BAD_SURFACE, EGL_FALSE); egl_surface_t const * const s = get_surface(surface); - EGLBoolean result(EGL_TRUE); - if (attribute == EGL_CONFIG_ID) { - // We need to remap EGL_CONFIG_IDs - *value = dp->configs[intptr_t(s->config)].configId; - } else { - result = s->cnx->egl.eglQuerySurface( - dp->disp[s->impl].dpy, s->surface, attribute, value); - } - - return result; + return s->cnx->egl.eglQuerySurface( + dp->disp.dpy, s->surface, attribute, value); } void EGLAPI eglBeginFrame(EGLDisplay dpy, EGLSurface surface) { @@ -514,9 +384,7 @@ EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, share_list = c->context; } EGLContext context = cnx->egl.eglCreateContext( - dp->disp[ dp->configs[intptr_t(config)].impl ].dpy, - dp->configs[intptr_t(config)].config, - share_list, attrib_list); + dp->disp.dpy, config, share_list, attrib_list); if (context != EGL_NO_CONTEXT) { // figure out if it's a GLESv1 or GLESv2 int version = 0; @@ -526,15 +394,14 @@ EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, GLint value = *attrib_list++; if (attr == EGL_CONTEXT_CLIENT_VERSION) { if (value == 1) { - version = GLESv1_INDEX; + version = egl_connection_t::GLESv1_INDEX; } else if (value == 2) { - version = GLESv2_INDEX; + version = egl_connection_t::GLESv2_INDEX; } } }; } - egl_context_t* c = new egl_context_t(dpy, context, config, - dp->configs[intptr_t(config)].impl, cnx, version); + egl_context_t* c = new egl_context_t(dpy, context, config, cnx, version); #if EGL_TRACE if (gEGLDebugLevel > 0) GLTrace_eglCreateContext(version, c); @@ -558,8 +425,7 @@ EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx) return setError(EGL_BAD_CONTEXT, EGL_FALSE); egl_context_t * const c = get_context(ctx); - EGLBoolean result = c->cnx->egl.eglDestroyContext( - dp->disp[c->impl].dpy, c->context); + EGLBoolean result = c->cnx->egl.eglDestroyContext(dp->disp.dpy, c->context); if (result == EGL_TRUE) { _c.terminate(); } @@ -625,20 +491,12 @@ EGLBoolean eglMakeCurrent( EGLDisplay dpy, EGLSurface draw, // retrieve the underlying implementation's draw EGLSurface if (draw != EGL_NO_SURFACE) { d = get_surface(draw); - // make sure the EGLContext and EGLSurface passed in are for - // the same driver - if (c && d->impl != c->impl) - return setError(EGL_BAD_MATCH, EGL_FALSE); impl_draw = d->surface; } // retrieve the underlying implementation's read EGLSurface if (read != EGL_NO_SURFACE) { r = get_surface(read); - // make sure the EGLContext and EGLSurface passed in are for - // the same driver - if (c && r->impl != c->impl) - return setError(EGL_BAD_MATCH, EGL_FALSE); impl_read = r->surface; } @@ -682,17 +540,9 @@ EGLBoolean eglQueryContext( EGLDisplay dpy, EGLContext ctx, if (!_c.get()) return setError(EGL_BAD_CONTEXT, EGL_FALSE); egl_context_t * const c = get_context(ctx); + return c->cnx->egl.eglQueryContext( + dp->disp.dpy, c->context, attribute, value); - EGLBoolean result(EGL_TRUE); - if (attribute == EGL_CONFIG_ID) { - *value = dp->configs[intptr_t(c->config)].configId; - } else { - // We need to remap EGL_CONFIG_IDs - result = c->cnx->egl.eglQueryContext( - dp->disp[c->impl].dpy, c->context, attribute, value); - } - - return result; } EGLContext eglGetCurrentContext(void) @@ -744,64 +594,37 @@ EGLDisplay eglGetCurrentDisplay(void) EGLBoolean eglWaitGL(void) { - // could be called before eglInitialize(), but we wouldn't have a context - // then, and this function would return GL_TRUE, which isn't wrong. - clearError(); - EGLBoolean res = EGL_TRUE; - EGLContext ctx = getContext(); - if (ctx) { - egl_context_t const * const c = get_context(ctx); - if (!c) return setError(EGL_BAD_CONTEXT, EGL_FALSE); - if (uint32_t(c->impl)>=2) - return setError(EGL_BAD_CONTEXT, EGL_FALSE); - egl_connection_t* const cnx = &gEGLImpl[c->impl]; - if (!cnx->dso) - return setError(EGL_BAD_CONTEXT, EGL_FALSE); - res = cnx->egl.eglWaitGL(); - } - return res; + egl_connection_t* const cnx = &gEGLImpl; + if (!cnx->dso) + return setError(EGL_BAD_CONTEXT, EGL_FALSE); + + return cnx->egl.eglWaitGL(); } EGLBoolean eglWaitNative(EGLint engine) { - // could be called before eglInitialize(), but we wouldn't have a context - // then, and this function would return GL_TRUE, which isn't wrong. - clearError(); - EGLBoolean res = EGL_TRUE; - EGLContext ctx = getContext(); - if (ctx) { - egl_context_t const * const c = get_context(ctx); - if (!c) return setError(EGL_BAD_CONTEXT, EGL_FALSE); - if (uint32_t(c->impl)>=2) - return setError(EGL_BAD_CONTEXT, EGL_FALSE); - egl_connection_t* const cnx = &gEGLImpl[c->impl]; - if (!cnx->dso) - return setError(EGL_BAD_CONTEXT, EGL_FALSE); - res = cnx->egl.eglWaitNative(engine); - } - return res; + egl_connection_t* const cnx = &gEGLImpl; + if (!cnx->dso) + return setError(EGL_BAD_CONTEXT, EGL_FALSE); + + return cnx->egl.eglWaitNative(engine); } EGLint eglGetError(void) { - EGLint result = EGL_SUCCESS; - EGLint err; - for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) { - err = EGL_SUCCESS; - egl_connection_t* const cnx = &gEGLImpl[i]; - if (cnx->dso) - err = cnx->egl.eglGetError(); - if (err!=EGL_SUCCESS && result==EGL_SUCCESS) - result = err; + EGLint err = EGL_SUCCESS; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso) { + err = cnx->egl.eglGetError(); } - err = egl_tls_t::getError(); - if (result == EGL_SUCCESS) - result = err; - return result; + if (err == EGL_SUCCESS) { + err = egl_tls_t::getError(); + } + return err; } // Note: Similar implementations of these functions also exist in @@ -885,20 +708,20 @@ __eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname) if (!addr && (slot < MAX_NUMBER_OF_GL_EXTENSIONS)) { bool found = false; - for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) { - egl_connection_t* const cnx = &gEGLImpl[i]; - if (cnx->dso && cnx->egl.eglGetProcAddress) { - found = true; - // Extensions are independent of the bound context - cnx->hooks[GLESv1_INDEX]->ext.extensions[slot] = - cnx->hooks[GLESv2_INDEX]->ext.extensions[slot] = + + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglGetProcAddress) { + found = true; + // Extensions are independent of the bound context + cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] = + cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = #if EGL_TRACE - debugHooks->ext.extensions[slot] = - gHooksTrace.ext.extensions[slot] = + debugHooks->ext.extensions[slot] = + gHooksTrace.ext.extensions[slot] = #endif - cnx->egl.eglGetProcAddress(procname); - } + cnx->egl.eglGetProcAddress(procname); } + if (found) { addr = gExtensionForwarders[slot]; @@ -937,7 +760,7 @@ EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw) #endif egl_surface_t const * const s = get_surface(draw); - return s->cnx->egl.eglSwapBuffers(dp->disp[s->impl].dpy, s->surface); + return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface); } EGLBoolean eglCopyBuffers( EGLDisplay dpy, EGLSurface surface, @@ -953,8 +776,7 @@ EGLBoolean eglCopyBuffers( EGLDisplay dpy, EGLSurface surface, return setError(EGL_BAD_SURFACE, EGL_FALSE); egl_surface_t const * const s = get_surface(surface); - return s->cnx->egl.eglCopyBuffers( - dp->disp[s->impl].dpy, s->surface, target); + return s->cnx->egl.eglCopyBuffers(dp->disp.dpy, s->surface, target); } const char* eglQueryString(EGLDisplay dpy, EGLint name) @@ -973,12 +795,8 @@ const char* eglQueryString(EGLDisplay dpy, EGLint name) return dp->getExtensionString(); case EGL_CLIENT_APIS: return dp->getClientApiString(); - case EGL_VERSION_HW_ANDROID: { - if (gEGLImpl[IMPL_HARDWARE].dso) { - return dp->disp[IMPL_HARDWARE].queryString.version; - } - return dp->disp[IMPL_SOFTWARE].queryString.version; - } + case EGL_VERSION_HW_ANDROID: + return dp->disp.queryString.version; } return setError(EGL_BAD_PARAMETER, (const char *)0); } @@ -1003,7 +821,7 @@ EGLBoolean eglSurfaceAttrib( egl_surface_t const * const s = get_surface(surface); if (s->cnx->egl.eglSurfaceAttrib) { return s->cnx->egl.eglSurfaceAttrib( - dp->disp[s->impl].dpy, s->surface, attribute, value); + dp->disp.dpy, s->surface, attribute, value); } return setError(EGL_BAD_SURFACE, EGL_FALSE); } @@ -1023,7 +841,7 @@ EGLBoolean eglBindTexImage( egl_surface_t const * const s = get_surface(surface); if (s->cnx->egl.eglBindTexImage) { return s->cnx->egl.eglBindTexImage( - dp->disp[s->impl].dpy, s->surface, buffer); + dp->disp.dpy, s->surface, buffer); } return setError(EGL_BAD_SURFACE, EGL_FALSE); } @@ -1043,7 +861,7 @@ EGLBoolean eglReleaseTexImage( egl_surface_t const * const s = get_surface(surface); if (s->cnx->egl.eglReleaseTexImage) { return s->cnx->egl.eglReleaseTexImage( - dp->disp[s->impl].dpy, s->surface, buffer); + dp->disp.dpy, s->surface, buffer); } return setError(EGL_BAD_SURFACE, EGL_FALSE); } @@ -1056,17 +874,11 @@ EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval) if (!dp) return EGL_FALSE; EGLBoolean res = EGL_TRUE; - for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) { - egl_connection_t* const cnx = &gEGLImpl[i]; - if (cnx->dso) { - if (cnx->egl.eglSwapInterval) { - if (cnx->egl.eglSwapInterval( - dp->disp[i].dpy, interval) == EGL_FALSE) { - res = EGL_FALSE; - } - } - } + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglSwapInterval) { + res = cnx->egl.eglSwapInterval(dp->disp.dpy, interval); } + return res; } @@ -1079,23 +891,15 @@ EGLBoolean eglWaitClient(void) { clearError(); - // could be called before eglInitialize(), but we wouldn't have a context - // then, and this function would return GL_TRUE, which isn't wrong. - EGLBoolean res = EGL_TRUE; - EGLContext ctx = getContext(); - if (ctx) { - egl_context_t const * const c = get_context(ctx); - if (!c) return setError(EGL_BAD_CONTEXT, EGL_FALSE); - if (uint32_t(c->impl)>=2) - return setError(EGL_BAD_CONTEXT, EGL_FALSE); - egl_connection_t* const cnx = &gEGLImpl[c->impl]; - if (!cnx->dso) - return setError(EGL_BAD_CONTEXT, EGL_FALSE); - if (cnx->egl.eglWaitClient) { - res = cnx->egl.eglWaitClient(); - } else { - res = cnx->egl.eglWaitGL(); - } + egl_connection_t* const cnx = &gEGLImpl; + if (!cnx->dso) + return setError(EGL_BAD_CONTEXT, EGL_FALSE); + + EGLBoolean res; + if (cnx->egl.eglWaitClient) { + res = cnx->egl.eglWaitClient(); + } else { + res = cnx->egl.eglWaitGL(); } return res; } @@ -1110,15 +914,9 @@ EGLBoolean eglBindAPI(EGLenum api) // bind this API on all EGLs EGLBoolean res = EGL_TRUE; - for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) { - egl_connection_t* const cnx = &gEGLImpl[i]; - if (cnx->dso) { - if (cnx->egl.eglBindAPI) { - if (cnx->egl.eglBindAPI(api) == EGL_FALSE) { - res = EGL_FALSE; - } - } - } + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglBindAPI) { + res = cnx->egl.eglBindAPI(api); } return res; } @@ -1131,16 +929,11 @@ EGLenum eglQueryAPI(void) return setError(EGL_BAD_PARAMETER, EGL_FALSE); } - for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) { - egl_connection_t* const cnx = &gEGLImpl[i]; - if (cnx->dso) { - if (cnx->egl.eglQueryAPI) { - // the first one we find is okay, because they all - // should be the same - return cnx->egl.eglQueryAPI(); - } - } + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglQueryAPI) { + return cnx->egl.eglQueryAPI(); } + // or, it can only be OpenGL ES return EGL_OPENGL_ES_API; } @@ -1152,14 +945,11 @@ EGLBoolean eglReleaseThread(void) // If there is context bound to the thread, release it egl_display_t::loseCurrent(get_context(getContext())); - for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) { - egl_connection_t* const cnx = &gEGLImpl[i]; - if (cnx->dso) { - if (cnx->egl.eglReleaseThread) { - cnx->egl.eglReleaseThread(); - } - } + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglReleaseThread) { + cnx->egl.eglReleaseThread(); } + egl_tls_t::clearTLS(); #if EGL_TRACE if (gEGLDebugLevel > 0) @@ -1179,9 +969,7 @@ EGLSurface eglCreatePbufferFromClientBuffer( if (!cnx) return EGL_FALSE; if (cnx->egl.eglCreatePbufferFromClientBuffer) { return cnx->egl.eglCreatePbufferFromClientBuffer( - dp->disp[ dp->configs[intptr_t(config)].impl ].dpy, - buftype, buffer, - dp->configs[intptr_t(config)].config, attrib_list); + dp->disp.dpy, buftype, buffer, config, attrib_list); } return setError(EGL_BAD_CONFIG, EGL_NO_SURFACE); } @@ -1205,7 +993,7 @@ EGLBoolean eglLockSurfaceKHR(EGLDisplay dpy, EGLSurface surface, egl_surface_t const * const s = get_surface(surface); if (s->cnx->egl.eglLockSurfaceKHR) { return s->cnx->egl.eglLockSurfaceKHR( - dp->disp[s->impl].dpy, s->surface, attrib_list); + dp->disp.dpy, s->surface, attrib_list); } return setError(EGL_BAD_DISPLAY, EGL_FALSE); } @@ -1223,8 +1011,7 @@ EGLBoolean eglUnlockSurfaceKHR(EGLDisplay dpy, EGLSurface surface) egl_surface_t const * const s = get_surface(surface); if (s->cnx->egl.eglUnlockSurfaceKHR) { - return s->cnx->egl.eglUnlockSurfaceKHR( - dp->disp[s->impl].dpy, s->surface); + return s->cnx->egl.eglUnlockSurfaceKHR(dp->disp.dpy, s->surface); } return setError(EGL_BAD_DISPLAY, EGL_FALSE); } @@ -1244,12 +1031,12 @@ EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target, egl_context_t * const c = get_context(ctx); // since we have an EGLContext, we know which implementation to use EGLImageKHR image = c->cnx->egl.eglCreateImageKHR( - dp->disp[c->impl].dpy, c->context, target, buffer, attrib_list); + dp->disp.dpy, c->context, target, buffer, attrib_list); if (image == EGL_NO_IMAGE_KHR) return image; egl_image_t* result = new egl_image_t(dpy, ctx); - result->images[c->impl] = image; + result->image = image; return (EGLImageKHR)result; } else { // EGL_NO_CONTEXT is a valid parameter @@ -1261,23 +1048,14 @@ EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLint currentError = eglGetError(); - EGLImageKHR implImages[IMPL_NUM_IMPLEMENTATIONS]; - bool success = false; - for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) { - egl_connection_t* const cnx = &gEGLImpl[i]; - implImages[i] = EGL_NO_IMAGE_KHR; - if (cnx->dso) { - if (cnx->egl.eglCreateImageKHR) { - implImages[i] = cnx->egl.eglCreateImageKHR( - dp->disp[i].dpy, ctx, target, buffer, attrib_list); - if (implImages[i] != EGL_NO_IMAGE_KHR) { - success = true; - } - } - } + EGLImageKHR implImage = EGL_NO_IMAGE_KHR; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglCreateImageKHR) { + implImage = cnx->egl.eglCreateImageKHR( + dp->disp.dpy, ctx, target, buffer, attrib_list); } - if (!success) { + if (implImage == EGL_NO_IMAGE_KHR) { // failure, if there was an error when we entered this function, // the error flag must not be updated. // Otherwise, the error is whatever happened in the implementation @@ -1289,13 +1067,12 @@ EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target, } else { // In case of success, we need to clear all error flags // (especially those caused by the implementation that didn't - // succeed). TODO: we could avoid this if we knew this was - // a "full" success (all implementation succeeded). + // succeed). eglGetError(); } egl_image_t* result = new egl_image_t(dpy, ctx); - memcpy(result->images, implImages, sizeof(implImages)); + result->image = implImage; return (EGLImageKHR)result; } } @@ -1312,19 +1089,17 @@ EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR img) egl_image_t* image = get_image(img); bool success = false; - for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) { - egl_connection_t* const cnx = &gEGLImpl[i]; - if (image->images[i] != EGL_NO_IMAGE_KHR) { - if (cnx->dso) { - if (cnx->egl.eglDestroyImageKHR) { - if (cnx->egl.eglDestroyImageKHR( - dp->disp[i].dpy, image->images[i])) { - success = true; - } - } + + egl_connection_t* const cnx = &gEGLImpl; + if (image->image != EGL_NO_IMAGE_KHR) { + if (cnx->dso && cnx->egl.eglDestroyImageKHR) { + if (cnx->egl.eglDestroyImageKHR( + dp->disp.dpy, image->image)) { + success = true; } } } + if (!success) return EGL_FALSE; @@ -1354,7 +1129,7 @@ EGLSyncKHR eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint *attrib_l EGLSyncKHR result = EGL_NO_SYNC_KHR; if (c->cnx->egl.eglCreateSyncKHR) { EGLSyncKHR sync = c->cnx->egl.eglCreateSyncKHR( - dp->disp[c->impl].dpy, type, attrib_list); + dp->disp.dpy, type, attrib_list); if (sync == EGL_NO_SYNC_KHR) return sync; result = (egl_sync_t*)new egl_sync_t(dpy, ctx, sync); @@ -1382,7 +1157,7 @@ EGLBoolean eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync) egl_context_t * const c = get_context(ctx); if (c->cnx->egl.eglDestroySyncKHR) { result = c->cnx->egl.eglDestroySyncKHR( - dp->disp[c->impl].dpy, syncObject->sync); + dp->disp.dpy, syncObject->sync); if (result) _s.terminate(); } @@ -1408,7 +1183,7 @@ EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTi egl_context_t * const c = get_context(ctx); if (c->cnx->egl.eglClientWaitSyncKHR) { return c->cnx->egl.eglClientWaitSyncKHR( - dp->disp[c->impl].dpy, syncObject->sync, flags, timeout); + dp->disp.dpy, syncObject->sync, flags, timeout); } return EGL_FALSE; @@ -1434,7 +1209,7 @@ EGLBoolean eglGetSyncAttribKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute egl_context_t * const c = get_context(ctx); if (c->cnx->egl.eglGetSyncAttribKHR) { return c->cnx->egl.eglGetSyncAttribKHR( - dp->disp[c->impl].dpy, syncObject->sync, attribute, value); + dp->disp.dpy, syncObject->sync, attribute, value); } return EGL_FALSE; @@ -1458,12 +1233,10 @@ EGLuint64NV eglGetSystemTimeFrequencyNV() } EGLuint64NV ret = 0; - egl_connection_t* const cnx = &gEGLImpl[IMPL_HARDWARE]; + egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso) { - if (cnx->egl.eglGetSystemTimeFrequencyNV) { - return cnx->egl.eglGetSystemTimeFrequencyNV(); - } + if (cnx->dso && cnx->egl.eglGetSystemTimeFrequencyNV) { + return cnx->egl.eglGetSystemTimeFrequencyNV(); } return setErrorQuiet(EGL_BAD_DISPLAY, 0); @@ -1478,12 +1251,10 @@ EGLuint64NV eglGetSystemTimeNV() } EGLuint64NV ret = 0; - egl_connection_t* const cnx = &gEGLImpl[IMPL_HARDWARE]; + egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso) { - if (cnx->egl.eglGetSystemTimeNV) { - return cnx->egl.eglGetSystemTimeNV(); - } + if (cnx->dso && cnx->egl.eglGetSystemTimeNV) { + return cnx->egl.eglGetSystemTimeNV(); } return setErrorQuiet(EGL_BAD_DISPLAY, 0); diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp index 7fd6519c2d..c79fb5f285 100644 --- a/opengl/libs/EGL/egl_cache.cpp +++ b/opengl/libs/EGL/egl_cache.cpp @@ -83,39 +83,39 @@ egl_cache_t* egl_cache_t::get() { void egl_cache_t::initialize(egl_display_t *display) { Mutex::Autolock lock(mMutex); - for (int i = 0; i < IMPL_NUM_IMPLEMENTATIONS; i++) { - egl_connection_t* const cnx = &gEGLImpl[i]; - if (cnx->dso && cnx->major >= 0 && cnx->minor >= 0) { - const char* exts = display->disp[i].queryString.extensions; - size_t bcExtLen = strlen(BC_EXT_STR); - size_t extsLen = strlen(exts); - bool equal = !strcmp(BC_EXT_STR, exts); - bool atStart = !strncmp(BC_EXT_STR " ", exts, bcExtLen+1); - bool atEnd = (bcExtLen+1) < extsLen && - !strcmp(" " BC_EXT_STR, exts + extsLen - (bcExtLen+1)); - bool inMiddle = strstr(exts, " " BC_EXT_STR " "); - if (equal || atStart || atEnd || inMiddle) { - PFNEGLSETBLOBCACHEFUNCSANDROIDPROC eglSetBlobCacheFuncsANDROID; - eglSetBlobCacheFuncsANDROID = - reinterpret_cast<PFNEGLSETBLOBCACHEFUNCSANDROIDPROC>( + + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->major >= 0 && cnx->minor >= 0) { + const char* exts = display->disp.queryString.extensions; + size_t bcExtLen = strlen(BC_EXT_STR); + size_t extsLen = strlen(exts); + bool equal = !strcmp(BC_EXT_STR, exts); + bool atStart = !strncmp(BC_EXT_STR " ", exts, bcExtLen+1); + bool atEnd = (bcExtLen+1) < extsLen && + !strcmp(" " BC_EXT_STR, exts + extsLen - (bcExtLen+1)); + bool inMiddle = strstr(exts, " " BC_EXT_STR " "); + if (equal || atStart || atEnd || inMiddle) { + PFNEGLSETBLOBCACHEFUNCSANDROIDPROC eglSetBlobCacheFuncsANDROID; + eglSetBlobCacheFuncsANDROID = + reinterpret_cast<PFNEGLSETBLOBCACHEFUNCSANDROIDPROC>( cnx->egl.eglGetProcAddress( "eglSetBlobCacheFuncsANDROID")); - if (eglSetBlobCacheFuncsANDROID == NULL) { - ALOGE("EGL_ANDROID_blob_cache advertised by display %d, " - "but unable to get eglSetBlobCacheFuncsANDROID", i); - continue; - } + if (eglSetBlobCacheFuncsANDROID == NULL) { + ALOGE("EGL_ANDROID_blob_cache advertised, " + "but unable to get eglSetBlobCacheFuncsANDROID"); + return; + } - eglSetBlobCacheFuncsANDROID(display->disp[i].dpy, - android::setBlob, android::getBlob); - EGLint err = cnx->egl.eglGetError(); - if (err != EGL_SUCCESS) { - ALOGE("eglSetBlobCacheFuncsANDROID resulted in an error: " - "%#x", err); - } + eglSetBlobCacheFuncsANDROID(display->disp.dpy, + android::setBlob, android::getBlob); + EGLint err = cnx->egl.eglGetError(); + if (err != EGL_SUCCESS) { + ALOGE("eglSetBlobCacheFuncsANDROID resulted in an error: " + "%#x", err); } } } + mInitialized = true; } diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp index 5cf5236b90..c85b4ce20a 100644 --- a/opengl/libs/EGL/egl_display.cpp +++ b/opengl/libs/EGL/egl_display.cpp @@ -60,18 +60,12 @@ static char const * const sExtensionString = extern void initEglTraceLevel(); extern void setGLHooksThreadSpecific(gl_hooks_t const *value); -static int cmp_configs(const void* a, const void *b) { - const egl_config_t& c0 = *(egl_config_t const *)a; - const egl_config_t& c1 = *(egl_config_t const *)b; - return c0<c1 ? -1 : (c1<c0 ? 1 : 0); -} - // ---------------------------------------------------------------------------- egl_display_t egl_display_t::sDisplay[NUM_DISPLAYS]; egl_display_t::egl_display_t() : - magic('_dpy'), numTotalConfigs(0), configs(0), refs(0) { + magic('_dpy'), refs(0) { } egl_display_t::~egl_display_t() { @@ -119,15 +113,13 @@ EGLDisplay egl_display_t::getDisplay(EGLNativeDisplayType display) { // get our driver loader Loader& loader(Loader::getInstance()); - for (int i = 0; i < IMPL_NUM_IMPLEMENTATIONS; i++) { - egl_connection_t* const cnx = &gEGLImpl[i]; - if (cnx->dso && disp[i].dpy == EGL_NO_DISPLAY) { - EGLDisplay dpy = cnx->egl.eglGetDisplay(display); - disp[i].dpy = dpy; - if (dpy == EGL_NO_DISPLAY) { - loader.close(cnx->dso); - cnx->dso = NULL; - } + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && disp.dpy == EGL_NO_DISPLAY) { + EGLDisplay dpy = cnx->egl.eglGetDisplay(display); + disp.dpy = dpy; + if (dpy == EGL_NO_DISPLAY) { + loader.close(cnx->dso); + cnx->dso = NULL; } } @@ -160,12 +152,11 @@ EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) { // initialize each EGL and // build our own extension string first, based on the extension we know // and the extension supported by our client implementation - for (int i = 0; i < IMPL_NUM_IMPLEMENTATIONS; i++) { - egl_connection_t* const cnx = &gEGLImpl[i]; - cnx->major = -1; - cnx->minor = -1; - if (!cnx->dso) - continue; + + egl_connection_t* const cnx = &gEGLImpl; + cnx->major = -1; + cnx->minor = -1; + if (cnx->dso) { #if defined(ADRENO130) #warning "Adreno-130 eglInitialize() workaround" @@ -177,31 +168,30 @@ EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) { * eglGetDisplay() before calling eglInitialize(); */ if (i == IMPL_HARDWARE) { - disp[i].dpy = - cnx->egl.eglGetDisplay(EGL_DEFAULT_DISPLAY); + disp[i].dpy = cnx->egl.eglGetDisplay(EGL_DEFAULT_DISPLAY); } #endif - EGLDisplay idpy = disp[i].dpy; + EGLDisplay idpy = disp.dpy; if (cnx->egl.eglInitialize(idpy, &cnx->major, &cnx->minor)) { - //ALOGD("initialized %d dpy=%p, ver=%d.%d, cnx=%p", - // i, idpy, cnx->major, cnx->minor, cnx); + //ALOGD("initialized dpy=%p, ver=%d.%d, cnx=%p", + // idpy, cnx->major, cnx->minor, cnx); // display is now initialized - disp[i].state = egl_display_t::INITIALIZED; + disp.state = egl_display_t::INITIALIZED; // get the query-strings for this display for each implementation - disp[i].queryString.vendor = cnx->egl.eglQueryString(idpy, + disp.queryString.vendor = cnx->egl.eglQueryString(idpy, EGL_VENDOR); - disp[i].queryString.version = cnx->egl.eglQueryString(idpy, + disp.queryString.version = cnx->egl.eglQueryString(idpy, EGL_VERSION); - disp[i].queryString.extensions = cnx->egl.eglQueryString(idpy, + disp.queryString.extensions = cnx->egl.eglQueryString(idpy, EGL_EXTENSIONS); - disp[i].queryString.clientApi = cnx->egl.eglQueryString(idpy, + disp.queryString.clientApi = cnx->egl.eglQueryString(idpy, EGL_CLIENT_APIS); } else { - ALOGW("%d: eglInitialize(%p) failed (%s)", i, idpy, + ALOGW("eglInitialize(%p) failed (%s)", idpy, egl_tls_t::egl_strerror(cnx->egl.eglGetError())); } } @@ -211,7 +201,7 @@ EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) { mVersionString.setTo(sVersionString); mClientApiString.setTo(sClientApiString); - // we only add extensions that exist in at least one implementation + // we only add extensions that exist in the implementation char const* start = sExtensionString; char const* end; do { @@ -223,15 +213,13 @@ EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) { if (len) { // NOTE: we could avoid the copy if we had strnstr. const String8 ext(start, len); - // now go through all implementations and look for this extension - for (int i = 0; i < IMPL_NUM_IMPLEMENTATIONS; i++) { - if (disp[i].queryString.extensions) { - // if we find it, add this extension string to our list - // (and don't forget the space) - const char* match = strstr(disp[i].queryString.extensions, ext.string()); - if (match && (match[len] == ' ' || match[len] == 0)) { - mExtensionString.append(start, len+1); - } + // now look for this extension + if (disp.queryString.extensions) { + // if we find it, add this extension string to our list + // (and don't forget the space) + const char* match = strstr(disp.queryString.extensions, ext.string()); + if (match && (match[len] == ' ' || match[len] == 0)) { + mExtensionString.append(start, len+1); } } } @@ -242,52 +230,12 @@ EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) { egl_cache_t::get()->initialize(this); - EGLBoolean res = EGL_FALSE; - for (int i = 0; i < IMPL_NUM_IMPLEMENTATIONS; i++) { - egl_connection_t* const cnx = &gEGLImpl[i]; - if (cnx->dso && cnx->major >= 0 && cnx->minor >= 0) { - EGLint n; - if (cnx->egl.eglGetConfigs(disp[i].dpy, 0, 0, &n)) { - disp[i].config = (EGLConfig*) malloc(sizeof(EGLConfig) * n); - if (disp[i].config) { - if (cnx->egl.eglGetConfigs(disp[i].dpy, disp[i].config, n, - &disp[i].numConfigs)) { - numTotalConfigs += n; - res = EGL_TRUE; - } - } - } - } - } - - if (res == EGL_TRUE) { - configs = new egl_config_t[numTotalConfigs]; - for (int i = 0, k = 0; i < IMPL_NUM_IMPLEMENTATIONS; i++) { - egl_connection_t* const cnx = &gEGLImpl[i]; - if (cnx->dso && cnx->major >= 0 && cnx->minor >= 0) { - for (int j = 0; j < disp[i].numConfigs; j++) { - configs[k].impl = i; - configs[k].config = disp[i].config[j]; - configs[k].configId = k + 1; // CONFIG_ID start at 1 - // store the implementation's CONFIG_ID - cnx->egl.eglGetConfigAttrib(disp[i].dpy, disp[i].config[j], - EGL_CONFIG_ID, &configs[k].implConfigId); - k++; - } - } - } - - // sort our configurations so we can do binary-searches - qsort(configs, numTotalConfigs, sizeof(egl_config_t), cmp_configs); - - refs++; - if (major != NULL) - *major = VERSION_MAJOR; - if (minor != NULL) - *minor = VERSION_MINOR; - return EGL_TRUE; - } - return setError(EGL_NOT_INITIALIZED, EGL_FALSE); + refs++; + if (major != NULL) + *major = VERSION_MAJOR; + if (minor != NULL) + *minor = VERSION_MINOR; + return EGL_TRUE; } EGLBoolean egl_display_t::terminate() { @@ -305,22 +253,15 @@ EGLBoolean egl_display_t::terminate() { } EGLBoolean res = EGL_FALSE; - for (int i = 0; i < IMPL_NUM_IMPLEMENTATIONS; i++) { - egl_connection_t* const cnx = &gEGLImpl[i]; - if (cnx->dso && disp[i].state == egl_display_t::INITIALIZED) { - if (cnx->egl.eglTerminate(disp[i].dpy) == EGL_FALSE) { - ALOGW("%d: eglTerminate(%p) failed (%s)", i, disp[i].dpy, - egl_tls_t::egl_strerror(cnx->egl.eglGetError())); - } - // REVISIT: it's unclear what to do if eglTerminate() fails - free(disp[i].config); - - disp[i].numConfigs = 0; - disp[i].config = 0; - disp[i].state = egl_display_t::TERMINATED; - - res = EGL_TRUE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && disp.state == egl_display_t::INITIALIZED) { + if (cnx->egl.eglTerminate(disp.dpy) == EGL_FALSE) { + ALOGW("eglTerminate(%p) failed (%s)", disp.dpy, + egl_tls_t::egl_strerror(cnx->egl.eglGetError())); } + // REVISIT: it's unclear what to do if eglTerminate() fails + disp.state = egl_display_t::TERMINATED; + res = EGL_TRUE; } // Mark all objects remaining in the list as terminated, unless @@ -337,50 +278,79 @@ EGLBoolean egl_display_t::terminate() { objects.clear(); refs--; - numTotalConfigs = 0; - delete[] configs; return res; } void egl_display_t::loseCurrent(egl_context_t * cur_c) { if (cur_c) { - egl_surface_t * cur_r = get_surface(cur_c->read); - egl_surface_t * cur_d = get_surface(cur_c->draw); - - // by construction, these are either 0 or valid (possibly terminated) - // it should be impossible for these to be invalid - ContextRef _cur_c(cur_c); - SurfaceRef _cur_r(cur_r); - SurfaceRef _cur_d(cur_d); + egl_display_t* display = cur_c->getDisplay(); + if (display) { + display->loseCurrentImpl(cur_c); + } + } +} +void egl_display_t::loseCurrentImpl(egl_context_t * cur_c) +{ + // by construction, these are either 0 or valid (possibly terminated) + // it should be impossible for these to be invalid + ContextRef _cur_c(cur_c); + SurfaceRef _cur_r(cur_c ? get_surface(cur_c->read) : NULL); + SurfaceRef _cur_d(cur_c ? get_surface(cur_c->draw) : NULL); + + { // scope for the lock + Mutex::Autolock _l(lock); cur_c->onLooseCurrent(); - _cur_c.release(); - _cur_r.release(); - _cur_d.release(); } + + // This cannot be called with the lock held because it might end-up + // calling back into EGL (in particular when a surface is destroyed + // it calls ANativeWindow::disconnect + _cur_c.release(); + _cur_r.release(); + _cur_d.release(); } EGLBoolean egl_display_t::makeCurrent(egl_context_t* c, egl_context_t* cur_c, EGLSurface draw, EGLSurface read, EGLContext ctx, EGLSurface impl_draw, EGLSurface impl_read, EGLContext impl_ctx) { - Mutex::Autolock _l(lock); EGLBoolean result; - if (c) { - result = c->cnx->egl.eglMakeCurrent( - disp[c->impl].dpy, impl_draw, impl_read, impl_ctx); - } else { - result = cur_c->cnx->egl.eglMakeCurrent( - disp[cur_c->impl].dpy, impl_draw, impl_read, impl_ctx); - } - if (result == EGL_TRUE) { - loseCurrent(cur_c); + + // by construction, these are either 0 or valid (possibly terminated) + // it should be impossible for these to be invalid + ContextRef _cur_c(cur_c); + SurfaceRef _cur_r(cur_c ? get_surface(cur_c->read) : NULL); + SurfaceRef _cur_d(cur_c ? get_surface(cur_c->draw) : NULL); + + { // scope for the lock + Mutex::Autolock _l(lock); if (c) { - c->onMakeCurrent(draw, read); + result = c->cnx->egl.eglMakeCurrent( + disp.dpy, impl_draw, impl_read, impl_ctx); + if (result == EGL_TRUE) { + c->onMakeCurrent(draw, read); + } + } else { + result = cur_c->cnx->egl.eglMakeCurrent( + disp.dpy, impl_draw, impl_read, impl_ctx); + if (result == EGL_TRUE) { + cur_c->onLooseCurrent(); + } } } + + if (result == EGL_TRUE) { + // This cannot be called with the lock held because it might end-up + // calling back into EGL (in particular when a surface is destroyed + // it calls ANativeWindow::disconnect + _cur_c.release(); + _cur_r.release(); + _cur_d.release(); + } + return result; } diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h index 4479e00ede..63482284fc 100644 --- a/opengl/libs/EGL/egl_display.h +++ b/opengl/libs/EGL/egl_display.h @@ -44,26 +44,10 @@ class egl_connection_t; // ---------------------------------------------------------------------------- -struct egl_config_t { - egl_config_t() {} - egl_config_t(int impl, EGLConfig config) - : impl(impl), config(config), configId(0), implConfigId(0) { } - int impl; // the implementation this config is for - EGLConfig config; // the implementation's EGLConfig - EGLint configId; // our CONFIG_ID - EGLint implConfigId; // the implementation's CONFIG_ID - inline bool operator < (const egl_config_t& rhs) const { - if (impl < rhs.impl) return true; - if (impl > rhs.impl) return false; - return config < rhs.config; - } -}; - -// ---------------------------------------------------------------------------- - class EGLAPI egl_display_t { // marked as EGLAPI for testing purposes static egl_display_t sDisplay[NUM_DISPLAYS]; EGLDisplay getDisplay(EGLNativeDisplayType display); + void loseCurrentImpl(egl_context_t * cur_c); public: enum { @@ -112,12 +96,9 @@ public: }; struct DisplayImpl { - DisplayImpl() : dpy(EGL_NO_DISPLAY), config(0), - state(NOT_INITIALIZED), numConfigs(0) { } + DisplayImpl() : dpy(EGL_NO_DISPLAY), state(NOT_INITIALIZED) { } EGLDisplay dpy; - EGLConfig* config; EGLint state; - EGLint numConfigs; strings_t queryString; }; @@ -125,9 +106,7 @@ private: uint32_t magic; public: - DisplayImpl disp[IMPL_NUM_IMPLEMENTATIONS]; - EGLint numTotalConfigs; - egl_config_t* configs; + DisplayImpl disp; private: uint32_t refs; diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp index b660c53142..d0cbb31b15 100644 --- a/opengl/libs/EGL/egl_object.cpp +++ b/opengl/libs/EGL/egl_object.cpp @@ -64,9 +64,9 @@ bool egl_object_t::get(egl_display_t const* display, egl_object_t* object) { // ---------------------------------------------------------------------------- egl_context_t::egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config, - int impl, egl_connection_t const* cnx, int version) : + egl_connection_t const* cnx, int version) : egl_object_t(get_display(dpy)), dpy(dpy), context(context), - config(config), read(0), draw(0), impl(impl), cnx(cnx), + config(config), read(0), draw(0), cnx(cnx), version(version) { } @@ -87,7 +87,7 @@ void egl_context_t::onMakeCurrent(EGLSurface draw, EGLSurface read) { if (gl_extensions.isEmpty()) { // call the implementation's glGetString(GL_EXTENSIONS) - const char* exts = (const char *)gEGLImpl[impl].hooks[version]->gl.glGetString(GL_EXTENSIONS); + const char* exts = (const char *)gEGLImpl.hooks[version]->gl.glGetString(GL_EXTENSIONS); gl_extensions.setTo(exts); if (gl_extensions.find("GL_EXT_debug_marker") < 0) { String8 temp("GL_EXT_debug_marker "); diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h index abd4cbb962..f137badd11 100644 --- a/opengl/libs/EGL/egl_object.h +++ b/opengl/libs/EGL/egl_object.h @@ -125,7 +125,7 @@ void egl_object_t::LocalRef<N,T>::terminate() { // ---------------------------------------------------------------------------- -class egl_surface_t: public egl_object_t { +class egl_surface_t : public egl_object_t { protected: ~egl_surface_t() { ANativeWindow* const window = win.get(); @@ -140,15 +140,14 @@ public: typedef egl_object_t::LocalRef<egl_surface_t, EGLSurface> Ref; egl_surface_t(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, - EGLSurface surface, int impl, egl_connection_t const* cnx) : + EGLSurface surface, egl_connection_t const* cnx) : egl_object_t(get_display(dpy)), dpy(dpy), surface(surface), - config(config), win(win), impl(impl), cnx(cnx) { + config(config), win(win), cnx(cnx) { } EGLDisplay dpy; EGLSurface surface; EGLConfig config; sp<ANativeWindow> win; - int impl; egl_connection_t const* cnx; }; @@ -159,7 +158,7 @@ public: typedef egl_object_t::LocalRef<egl_context_t, EGLContext> Ref; egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config, - int impl, egl_connection_t const* cnx, int version); + egl_connection_t const* cnx, int version); void onLooseCurrent(); void onMakeCurrent(EGLSurface draw, EGLSurface read); @@ -169,7 +168,6 @@ public: EGLConfig config; EGLSurface read; EGLSurface draw; - int impl; egl_connection_t const* cnx; int version; String8 gl_extensions; @@ -182,12 +180,11 @@ public: typedef egl_object_t::LocalRef<egl_image_t, EGLImageKHR> Ref; egl_image_t(EGLDisplay dpy, EGLContext context) : - egl_object_t(get_display(dpy)), dpy(dpy), context(context) { - memset(images, 0, sizeof(images)); - } + egl_object_t(get_display(dpy)), + dpy(dpy), context(context), image(EGL_NO_IMAGE_KHR) { } EGLDisplay dpy; EGLContext context; - EGLImageKHR images[IMPL_NUM_IMPLEMENTATIONS]; + EGLImageKHR image; }; class egl_sync_t: public egl_object_t { diff --git a/opengl/libs/EGL/egldefs.h b/opengl/libs/EGL/egldefs.h index ff20957f3e..c900c1c5fe 100644 --- a/opengl/libs/EGL/egldefs.h +++ b/opengl/libs/EGL/egldefs.h @@ -19,31 +19,24 @@ #include "hooks.h" +#define VERSION_MAJOR 1 +#define VERSION_MINOR 4 + // ---------------------------------------------------------------------------- namespace android { // ---------------------------------------------------------------------------- -#define VERSION_MAJOR 1 -#define VERSION_MINOR 4 - // EGLDisplay are global, not attached to a given thread const unsigned int NUM_DISPLAYS = 1; -enum { - IMPL_HARDWARE = 0, - IMPL_SOFTWARE, - IMPL_NUM_IMPLEMENTATIONS -}; - -enum { - GLESv1_INDEX = 0, - GLESv2_INDEX = 1, -}; - // ---------------------------------------------------------------------------- -struct egl_connection_t -{ +struct egl_connection_t { + enum { + GLESv1_INDEX = 0, + GLESv2_INDEX = 1 + }; + inline egl_connection_t() : dso(0) { } void * dso; gl_hooks_t * hooks[2]; @@ -54,7 +47,7 @@ struct egl_connection_t // ---------------------------------------------------------------------------- -extern gl_hooks_t gHooks[2][IMPL_NUM_IMPLEMENTATIONS]; +extern gl_hooks_t gHooks[2]; extern gl_hooks_t gHooksNoContext; extern pthread_key_t gGLWrapperKey; extern "C" void gl_unimplemented(); @@ -63,7 +56,7 @@ extern "C" void gl_noop(); extern char const * const gl_names[]; extern char const * const egl_names[]; -extern egl_connection_t gEGLImpl[IMPL_NUM_IMPLEMENTATIONS]; +extern egl_connection_t gEGLImpl; // ---------------------------------------------------------------------------- }; // namespace android diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk index fc211e56b9..42e280fa9d 100644 --- a/services/surfaceflinger/Android.mk +++ b/services/surfaceflinger/Android.mk @@ -32,7 +32,7 @@ ifeq ($(TARGET_BOARD_PLATFORM), omap3) endif ifeq ($(TARGET_BOARD_PLATFORM), omap4) LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY - LOCAL_CFLAGS += -DUSE_TRIPLE_BUFFERING=1 + LOCAL_CFLAGS += -DUSE_TRIPLE_BUFFERING endif ifeq ($(TARGET_BOARD_PLATFORM), s5pc110) LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY -DNEVER_DEFAULT_TO_ASYNC_MODE diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp index f4afeeaa0a..69f1aca3d9 100644 --- a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp +++ b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 The Android Open Source Project + * Copyright (C) 2012 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. @@ -14,7 +14,6 @@ * limitations under the License. */ -#include <assert.h> #include <errno.h> #include <stdlib.h> #include <stdio.h> @@ -22,15 +21,6 @@ #include <unistd.h> #include <fcntl.h> -#include <signal.h> -#include <termios.h> -#include <sys/ioctl.h> -#include <sys/mman.h> -#include <sys/time.h> -#include <sys/types.h> -#include <sys/resource.h> - -#include <linux/unistd.h> #include <utils/Log.h> @@ -45,39 +35,22 @@ static char const * const kWakeFileName = "/sys/power/wait_for_fb_wake"; // ---------------------------------------------------------------------------- -DisplayHardwareBase::DisplayEventThreadBase::DisplayEventThreadBase( +DisplayHardwareBase::DisplayEventThread::DisplayEventThread( const sp<SurfaceFlinger>& flinger) : Thread(false), mFlinger(flinger) { } -DisplayHardwareBase::DisplayEventThreadBase::~DisplayEventThreadBase() { +DisplayHardwareBase::DisplayEventThread::~DisplayEventThread() { } -// ---------------------------------------------------------------------------- - -DisplayHardwareBase::DisplayEventThread::DisplayEventThread( - const sp<SurfaceFlinger>& flinger) - : DisplayEventThreadBase(flinger) -{ +status_t DisplayHardwareBase::DisplayEventThread::initCheck() const { + return ((access(kSleepFileName, R_OK) == 0 && + access(kWakeFileName, R_OK) == 0)) ? NO_ERROR : NO_INIT; } -DisplayHardwareBase::DisplayEventThread::~DisplayEventThread() -{ -} +bool DisplayHardwareBase::DisplayEventThread::threadLoop() { -bool DisplayHardwareBase::DisplayEventThread::threadLoop() -{ - int err = 0; - char buf; - int fd; - - fd = open(kSleepFileName, O_RDONLY, 0); - do { - err = read(fd, &buf, 1); - } while (err < 0 && errno == EINTR); - close(fd); - ALOGW_IF(err<0, "ANDROID_WAIT_FOR_FB_SLEEP failed (%s)", strerror(errno)); - if (err >= 0) { + if (waitForFbSleep() == NO_ERROR) { sp<SurfaceFlinger> flinger = mFlinger.promote(); ALOGD("About to give-up screen, flinger = %p", flinger.get()); if (flinger != 0) { @@ -85,39 +58,51 @@ bool DisplayHardwareBase::DisplayEventThread::threadLoop() flinger->screenReleased(0); mBarrier.wait(); } + if (waitForFbWake() == NO_ERROR) { + sp<SurfaceFlinger> flinger = mFlinger.promote(); + ALOGD("Screen about to return, flinger = %p", flinger.get()); + if (flinger != 0) { + flinger->screenAcquired(0); + } + return true; + } } - fd = open(kWakeFileName, O_RDONLY, 0); + + // error, exit the thread + return false; +} + +status_t DisplayHardwareBase::DisplayEventThread::waitForFbSleep() { + int err = 0; + char buf; + int fd = open(kSleepFileName, O_RDONLY, 0); + // if the file doesn't exist, the error will be caught in read() below do { - err = read(fd, &buf, 1); + err = read(fd, &buf, 1); } while (err < 0 && errno == EINTR); close(fd); - ALOGW_IF(err<0, "ANDROID_WAIT_FOR_FB_WAKE failed (%s)", strerror(errno)); - if (err >= 0) { - sp<SurfaceFlinger> flinger = mFlinger.promote(); - ALOGD("Screen about to return, flinger = %p", flinger.get()); - if (flinger != 0) - flinger->screenAcquired(0); - } - return true; + ALOGE_IF(err<0, "*** ANDROID_WAIT_FOR_FB_SLEEP failed (%s)", strerror(errno)); + return err < 0 ? -errno : int(NO_ERROR); } -status_t DisplayHardwareBase::DisplayEventThread::releaseScreen() const -{ - mBarrier.open(); - return NO_ERROR; +status_t DisplayHardwareBase::DisplayEventThread::waitForFbWake() { + int err = 0; + char buf; + int fd = open(kWakeFileName, O_RDONLY, 0); + // if the file doesn't exist, the error will be caught in read() below + do { + err = read(fd, &buf, 1); + } while (err < 0 && errno == EINTR); + close(fd); + ALOGE_IF(err<0, "*** ANDROID_WAIT_FOR_FB_WAKE failed (%s)", strerror(errno)); + return err < 0 ? -errno : int(NO_ERROR); } -status_t DisplayHardwareBase::DisplayEventThread::readyToRun() -{ +status_t DisplayHardwareBase::DisplayEventThread::releaseScreen() const { + mBarrier.open(); return NO_ERROR; } -status_t DisplayHardwareBase::DisplayEventThread::initCheck() const -{ - return ((access(kSleepFileName, R_OK) == 0 && - access(kWakeFileName, R_OK) == 0)) ? NO_ERROR : NO_INIT; -} - // ---------------------------------------------------------------------------- DisplayHardwareBase::DisplayHardwareBase(const sp<SurfaceFlinger>& flinger, @@ -127,35 +112,35 @@ DisplayHardwareBase::DisplayHardwareBase(const sp<SurfaceFlinger>& flinger, mDisplayEventThread = new DisplayEventThread(flinger); } -DisplayHardwareBase::~DisplayHardwareBase() -{ +void DisplayHardwareBase::startSleepManagement() const { + if (mDisplayEventThread->initCheck() == NO_ERROR) { + mDisplayEventThread->run("DisplayEventThread", PRIORITY_URGENT_DISPLAY); + } else { + ALOGW("/sys/power/wait_for_fb_{wake|sleep} don't exist"); + } +} + +DisplayHardwareBase::~DisplayHardwareBase() { // request exit mDisplayEventThread->requestExitAndWait(); } -bool DisplayHardwareBase::canDraw() const -{ +bool DisplayHardwareBase::canDraw() const { return mScreenAcquired; } -void DisplayHardwareBase::releaseScreen() const -{ +void DisplayHardwareBase::releaseScreen() const { status_t err = mDisplayEventThread->releaseScreen(); if (err >= 0) { mScreenAcquired = false; } } -void DisplayHardwareBase::acquireScreen() const -{ - status_t err = mDisplayEventThread->acquireScreen(); - if (err >= 0) { - mScreenAcquired = true; - } +void DisplayHardwareBase::acquireScreen() const { + mScreenAcquired = true; } -bool DisplayHardwareBase::isScreenAcquired() const -{ +bool DisplayHardwareBase::isScreenAcquired() const { return mScreenAcquired; } diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h index ef2df432ce..fba211beeb 100644 --- a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h +++ b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 The Android Open Source Project + * Copyright (C) 2012 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. @@ -20,8 +20,6 @@ #include <stdint.h> #include <utils/RefBase.h> #include <utils/threads.h> -#include <linux/kd.h> -#include <linux/vt.h> #include "Barrier.h" namespace android { @@ -31,11 +29,13 @@ class SurfaceFlinger; class DisplayHardwareBase { public: - DisplayHardwareBase( - const sp<SurfaceFlinger>& flinger, - uint32_t displayIndex); + DisplayHardwareBase( + const sp<SurfaceFlinger>& flinger, + uint32_t displayIndex); - ~DisplayHardwareBase(); + ~DisplayHardwareBase(); + + void startSleepManagement() const; // console management void releaseScreen() const; @@ -46,34 +46,21 @@ public: private: - class DisplayEventThreadBase : public Thread { - protected: + class DisplayEventThread : public Thread { wp<SurfaceFlinger> mFlinger; - public: - DisplayEventThreadBase(const sp<SurfaceFlinger>& flinger); - virtual ~DisplayEventThreadBase(); - virtual void onFirstRef() { - run("DisplayEventThread", PRIORITY_URGENT_DISPLAY); - } - virtual status_t acquireScreen() const { return NO_ERROR; }; - virtual status_t releaseScreen() const { return NO_ERROR; }; - virtual status_t initCheck() const = 0; - }; - - class DisplayEventThread : public DisplayEventThreadBase - { mutable Barrier mBarrier; + status_t waitForFbSleep(); + status_t waitForFbWake(); public: - DisplayEventThread(const sp<SurfaceFlinger>& flinger); + DisplayEventThread(const sp<SurfaceFlinger>& flinger); virtual ~DisplayEventThread(); virtual bool threadLoop(); - virtual status_t readyToRun(); - virtual status_t releaseScreen() const; - virtual status_t initCheck() const; + status_t releaseScreen() const; + status_t initCheck() const; }; - sp<DisplayEventThreadBase> mDisplayEventThread; - mutable int mScreenAcquired; + sp<DisplayEventThread> mDisplayEventThread; + mutable int mScreenAcquired; }; }; // namespace android diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 9c04d59720..3e6b872524 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -55,6 +55,7 @@ Layer::Layer(SurfaceFlinger* flinger, mCurrentTransform(0), mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), mCurrentOpacity(true), + mRefreshPending(0), mFrameLatencyNeeded(false), mFrameLatencyOffset(0), mFormat(PIXEL_FORMAT_NONE), @@ -113,7 +114,7 @@ Layer::~Layer() void Layer::onFrameQueued() { android_atomic_inc(&mQueuedFrames); - mFlinger->signalEvent(); + mFlinger->signalLayerUpdate(); } // called with SurfaceFlinger::mStateLock as soon as the layer is entered @@ -407,16 +408,37 @@ bool Layer::isCropped() const { // pageflip handling... // ---------------------------------------------------------------------------- +bool Layer::onPreComposition() +{ + // if there was more than one pending update, request a refresh + if (mRefreshPending >= 2) { + mRefreshPending = 0; + return true; + } + mRefreshPending = 0; + return false; +} + void Layer::lockPageFlip(bool& recomputeVisibleRegions) { if (mQueuedFrames > 0) { + + // if we've already called updateTexImage() without going through + // a composition step, we have to skip this layer at this point + // because we cannot call updateTeximage() without a corresponding + // compositionComplete() call. + // we'll trigger an update in onPreComposition(). + if (mRefreshPending++) { + return; + } + // Capture the old state of the layer for comparisons later const bool oldOpacity = isOpaque(); sp<GraphicBuffer> oldActiveBuffer = mActiveBuffer; // signal another event if we have more frames pending if (android_atomic_dec(&mQueuedFrames) > 1) { - mFlinger->signalEvent(); + mFlinger->signalLayerUpdate(); } if (mSurfaceTexture->updateTexImage() < NO_ERROR) { @@ -519,6 +541,10 @@ void Layer::lockPageFlip(bool& recomputeVisibleRegions) void Layer::unlockPageFlip( const Transform& planeTransform, Region& outDirtyRegion) { + if (mRefreshPending >= 2) { + return; + } + Region dirtyRegion(mPostedDirtyRegion); if (!dirtyRegion.isEmpty()) { mPostedDirtyRegion.clear(); @@ -552,9 +578,9 @@ void Layer::dump(String8& result, char* buffer, size_t SIZE) const snprintf(buffer, SIZE, " " "format=%2d, activeBuffer=[%4ux%4u:%4u,%3X]," - " transform-hint=0x%02x, queued-frames=%d\n", + " transform-hint=0x%02x, queued-frames=%d, mRefreshPending=%d\n", mFormat, w0, h0, s0,f0, - getTransformHint(), mQueuedFrames); + getTransformHint(), mQueuedFrames, mRefreshPending); result.append(buffer); diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 2dd4da130d..bf30608c97 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -80,6 +80,7 @@ public: virtual wp<IBinder> getSurfaceTextureBinder() const; virtual void onLayerDisplayed(); + virtual bool onPreComposition(); // only for debugging inline const sp<GraphicBuffer>& getActiveBuffer() const { return mActiveBuffer; } @@ -115,14 +116,17 @@ private: uint32_t mCurrentTransform; uint32_t mCurrentScalingMode; bool mCurrentOpacity; + size_t mRefreshPending; bool mFrameLatencyNeeded; int mFrameLatencyOffset; + struct Statistics { Statistics() : timestamp(0), set(0), vsync(0) { } nsecs_t timestamp; // buffer timestamp nsecs_t set; // buffer displayed timestamp nsecs_t vsync; // vsync immediately before set }; + // protected by mLock Statistics mFrameStats[128]; diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp index d32fcdd625..e764001206 100644 --- a/services/surfaceflinger/LayerBase.cpp +++ b/services/surfaceflinger/LayerBase.cpp @@ -47,8 +47,7 @@ LayerBase::LayerBase(SurfaceFlinger* flinger, DisplayID display) mOrientation(0), mPlaneOrientation(0), mTransactionFlags(0), - mPremultipliedAlpha(true), mName("unnamed"), mDebug(false), - mInvalidate(0) + mPremultipliedAlpha(true), mName("unnamed"), mDebug(false) { const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware()); mFlags = hw.getFlags(); @@ -262,23 +261,11 @@ void LayerBase::validateVisibility(const Transform& planeTransform) mTransformedBounds = tr.makeBounds(w, h); } -void LayerBase::lockPageFlip(bool& recomputeVisibleRegions) -{ +void LayerBase::lockPageFlip(bool& recomputeVisibleRegions) { } void LayerBase::unlockPageFlip( - const Transform& planeTransform, Region& outDirtyRegion) -{ - if ((android_atomic_and(~1, &mInvalidate)&1) == 1) { - outDirtyRegion.orSelf(visibleRegionScreen); - } -} - -void LayerBase::invalidate() -{ - if ((android_atomic_or(1, &mInvalidate)&1) == 0) { - mFlinger->signalEvent(); - } + const Transform& planeTransform, Region& outDirtyRegion) { } void LayerBase::drawRegion(const Region& reg) const @@ -471,16 +458,21 @@ void LayerBase::drawWithOpenGL(const Region& clip) const void LayerBase::dump(String8& result, char* buffer, size_t SIZE) const { const Layer::State& s(drawingState()); + + snprintf(buffer, SIZE, + "+ %s %p (%s)\n", + getTypeId(), this, getName().string()); + result.append(buffer); + s.transparentRegion.dump(result, "transparentRegion"); transparentRegionScreen.dump(result, "transparentRegionScreen"); visibleRegionScreen.dump(result, "visibleRegionScreen"); + snprintf(buffer, SIZE, - "+ %s %p (%s)\n" " " "z=%9d, pos=(%g,%g), size=(%4d,%4d), " "isOpaque=%1d, needsDithering=%1d, invalidate=%1d, " "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n", - getTypeId(), this, getName().string(), s.z, s.transform.tx(), s.transform.ty(), s.w, s.h, isOpaque(), needsDithering(), contentDirty, s.alpha, s.flags, diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h index 6b62c9db78..b8f7680b2b 100644 --- a/services/surfaceflinger/LayerBase.h +++ b/services/surfaceflinger/LayerBase.h @@ -103,8 +103,6 @@ public: Rect visibleBounds() const; void drawRegion(const Region& reg) const; - void invalidate(); - virtual sp<LayerBaseClient> getLayerBaseClient() const { return 0; } virtual sp<Layer> getLayer() const { return 0; } @@ -204,9 +202,16 @@ public: /** called with the state lock when the surface is removed from the * current list */ - virtual void onRemoved() { }; + virtual void onRemoved() { } - virtual void onLayerDisplayed() { }; + /** called after page-flip + */ + virtual void onLayerDisplayed() { } + + /** called before composition. + * returns true if the layer has pending updates. + */ + virtual bool onPreComposition() { return false; } /** always call base class first */ virtual void dump(String8& result, char* scratch, size_t size) const; @@ -275,10 +280,6 @@ protected: mutable bool mDebug; - // atomic - volatile int32_t mInvalidate; - - public: // called from class SurfaceFlinger virtual ~LayerBase(); diff --git a/services/surfaceflinger/MessageQueue.cpp b/services/surfaceflinger/MessageQueue.cpp index 70711e73ee..290fff469d 100644 --- a/services/surfaceflinger/MessageQueue.cpp +++ b/services/surfaceflinger/MessageQueue.cpp @@ -29,6 +29,7 @@ #include "MessageQueue.h" #include "EventThread.h" +#include "SurfaceFlinger.h" namespace android { @@ -48,14 +49,47 @@ void MessageBase::handleMessage(const Message&) { // --------------------------------------------------------------------------- +void MessageQueue::Handler::signalRefresh() { + if ((android_atomic_or(eventMaskRefresh, &mEventMask) & eventMaskRefresh) == 0) { + mQueue.mLooper->sendMessage(this, Message(MessageQueue::REFRESH)); + } +} + +void MessageQueue::Handler::signalInvalidate() { + if ((android_atomic_or(eventMaskInvalidate, &mEventMask) & eventMaskInvalidate) == 0) { + mQueue.mLooper->sendMessage(this, Message(MessageQueue::INVALIDATE)); + } +} + +void MessageQueue::Handler::handleMessage(const Message& message) { + switch (message.what) { + case INVALIDATE: + android_atomic_and(~eventMaskInvalidate, &mEventMask); + mQueue.mFlinger->onMessageReceived(message.what); + break; + case REFRESH: + android_atomic_and(~eventMaskRefresh, &mEventMask); + mQueue.mFlinger->onMessageReceived(message.what); + break; + } +} + +// --------------------------------------------------------------------------- + MessageQueue::MessageQueue() - : mLooper(new Looper(true)), mWorkPending(0) { } MessageQueue::~MessageQueue() { } +void MessageQueue::init(const sp<SurfaceFlinger>& flinger) +{ + mFlinger = flinger; + mLooper = new Looper(true); + mHandler = new Handler(*this); +} + void MessageQueue::setEventThread(const sp<EventThread>& eventThread) { mEventThread = eventThread; @@ -68,25 +102,16 @@ void MessageQueue::setEventThread(const sp<EventThread>& eventThread) void MessageQueue::waitMessage() { do { IPCThreadState::self()->flushCommands(); - int32_t ret = mLooper->pollOnce(-1); switch (ret) { case ALOOPER_POLL_WAKE: case ALOOPER_POLL_CALLBACK: - // callback and/or wake - if (android_atomic_and(0, &mWorkPending)) { - return; - } continue; - - case ALOOPER_POLL_TIMEOUT: - // timeout (should not happen) - continue; - case ALOOPER_POLL_ERROR: ALOGE("ALOOPER_POLL_ERROR"); + case ALOOPER_POLL_TIMEOUT: + // timeout (should not happen) continue; - default: // should not happen ALOGE("Looper::pollOnce() returned unknown status %d", ret); @@ -107,15 +132,13 @@ status_t MessageQueue::postMessage( return NO_ERROR; } -void MessageQueue::scheduleWorkASAP() { - if (android_atomic_or(1, &mWorkPending) == 0) { - mLooper->wake(); - } +void MessageQueue::invalidate() { +// mHandler->signalInvalidate(); + mEvents->requestNextVsync(); } -status_t MessageQueue::invalidate() { +void MessageQueue::refresh() { mEvents->requestNextVsync(); - return NO_ERROR; } int MessageQueue::cb_eventReceiver(int fd, int events, void* data) { @@ -126,10 +149,10 @@ int MessageQueue::cb_eventReceiver(int fd, int events, void* data) { int MessageQueue::eventReceiver(int fd, int events) { ssize_t n; DisplayEventReceiver::Event buffer[8]; - while ((n = getEvents(buffer, 8)) > 0) { + while ((n = DisplayEventReceiver::getEvents(mEventTube, buffer, 8)) > 0) { for (int i=0 ; i<n ; i++) { if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) { - scheduleWorkASAP(); + mHandler->signalRefresh(); break; } } @@ -137,24 +160,6 @@ int MessageQueue::eventReceiver(int fd, int events) { return 1; } -ssize_t MessageQueue::getEvents( - DisplayEventReceiver::Event* events, size_t count) -{ - ssize_t size = mEventTube->read(events, sizeof(events[0])*count); - ALOGE_IF(size<0, "MessageQueue::getEvents error (%s)", strerror(-size)); - if (size >= 0) { - // Note: if (size % sizeof(events[0])) != 0, we've got a - // partial read. This can happen if the queue filed up (ie: if we - // didn't pull from it fast enough). - // We discard the partial event and rely on the sender to - // re-send the event if appropriate (some events, like VSYNC - // can be lost forever). - // returns number of events read - size /= sizeof(events[0]); - } - return size; -} - // --------------------------------------------------------------------------- }; // namespace android diff --git a/services/surfaceflinger/MessageQueue.h b/services/surfaceflinger/MessageQueue.h index 5ea197dc70..ea29e7ef72 100644 --- a/services/surfaceflinger/MessageQueue.h +++ b/services/surfaceflinger/MessageQueue.h @@ -33,6 +33,7 @@ namespace android { class IDisplayEventConnection; class EventThread; +class SurfaceFlinger; // --------------------------------------------------------------------------- @@ -59,25 +60,48 @@ private: // --------------------------------------------------------------------------- class MessageQueue { + class Handler : public MessageHandler { + enum { + eventMaskInvalidate = 0x1, + eventMaskRefresh = 0x2 + }; + MessageQueue& mQueue; + int32_t mEventMask; + public: + Handler(MessageQueue& queue) : mQueue(queue), mEventMask(0) { } + virtual void handleMessage(const Message& message); + void signalRefresh(); + void signalInvalidate(); + }; + + friend class Handler; + + sp<SurfaceFlinger> mFlinger; sp<Looper> mLooper; sp<EventThread> mEventThread; sp<IDisplayEventConnection> mEvents; sp<BitTube> mEventTube; - int32_t mWorkPending; + sp<Handler> mHandler; + static int cb_eventReceiver(int fd, int events, void* data); int eventReceiver(int fd, int events); - ssize_t getEvents(DisplayEventReceiver::Event* events, size_t count); - void scheduleWorkASAP(); public: + enum { + INVALIDATE = 0, + REFRESH = 1, + }; + MessageQueue(); ~MessageQueue(); + void init(const sp<SurfaceFlinger>& flinger); void setEventThread(const sp<EventThread>& events); void waitMessage(); status_t postMessage(const sp<MessageBase>& message, nsecs_t reltime=0); - status_t invalidate(); + void invalidate(); + void refresh(); }; // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index ff70ec327e..40717f4e95 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -125,11 +125,34 @@ void SurfaceFlinger::init() ALOGI_IF(mDebugDDMS, "DDMS debugging enabled"); } +void SurfaceFlinger::onFirstRef() +{ + mEventQueue.init(this); + + run("SurfaceFlinger", PRIORITY_URGENT_DISPLAY); + + // Wait for the main thread to be done with its initialization + mReadyToRunBarrier.wait(); +} + + SurfaceFlinger::~SurfaceFlinger() { glDeleteTextures(1, &mWormholeTexName); } +void SurfaceFlinger::binderDied(const wp<IBinder>& who) +{ + // the window manager died on us. prepare its eulogy. + + // reset screen orientation + Vector<ComposerState> state; + setTransactionState(state, eOrientationDefault, 0); + + // restart the boot-animation + property_set("ctl.start", "bootanim"); +} + sp<IMemoryHeap> SurfaceFlinger::getCblk() const { return mServerHeap; @@ -183,26 +206,6 @@ void SurfaceFlinger::bootFinished() property_set("ctl.stop", "bootanim"); } -void SurfaceFlinger::binderDied(const wp<IBinder>& who) -{ - // the window manager died on us. prepare its eulogy. - - // reset screen orientation - Vector<ComposerState> state; - setTransactionState(state, eOrientationDefault, 0); - - // restart the boot-animation - property_set("ctl.start", "bootanim"); -} - -void SurfaceFlinger::onFirstRef() -{ - run("SurfaceFlinger", PRIORITY_URGENT_DISPLAY); - - // Wait for the main thread to be done with its initialization - mReadyToRunBarrier.wait(); -} - static inline uint16_t pack565(int r, int g, int b) { return (r<<11)|(g<<5)|b; } @@ -297,6 +300,7 @@ status_t SurfaceFlinger::readyToRun() // start the EventThread mEventThread = new EventThread(this); mEventQueue.setEventThread(mEventThread); + hw.startSleepManagement(); /* * We're now ready to accept clients... @@ -311,34 +315,6 @@ status_t SurfaceFlinger::readyToRun() } // ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#pragma mark Events Handler -#endif - -void SurfaceFlinger::waitForEvent() { - mEventQueue.waitMessage(); -} - -void SurfaceFlinger::signalEvent() { - mEventQueue.invalidate(); -} - -status_t SurfaceFlinger::postMessageAsync(const sp<MessageBase>& msg, - nsecs_t reltime, uint32_t flags) { - return mEventQueue.postMessage(msg, reltime); -} - -status_t SurfaceFlinger::postMessageSync(const sp<MessageBase>& msg, - nsecs_t reltime, uint32_t flags) { - status_t res = mEventQueue.postMessage(msg, reltime); - if (res == NO_ERROR) { - msg->wait(); - } - return res; -} - -// ---------------------------------------------------------------------------- bool SurfaceFlinger::authenticateSurfaceTexture( const sp<ISurfaceTexture>& surfaceTexture) const { @@ -388,59 +364,104 @@ sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection() { } // ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#pragma mark Main loop -#endif + +void SurfaceFlinger::waitForEvent() { + mEventQueue.waitMessage(); +} + +void SurfaceFlinger::signalTransaction() { + mEventQueue.invalidate(); +} + +void SurfaceFlinger::signalLayerUpdate() { + mEventQueue.invalidate(); +} + +void SurfaceFlinger::signalRefresh() { + mEventQueue.refresh(); +} + +status_t SurfaceFlinger::postMessageAsync(const sp<MessageBase>& msg, + nsecs_t reltime, uint32_t flags) { + return mEventQueue.postMessage(msg, reltime); +} + +status_t SurfaceFlinger::postMessageSync(const sp<MessageBase>& msg, + nsecs_t reltime, uint32_t flags) { + status_t res = mEventQueue.postMessage(msg, reltime); + if (res == NO_ERROR) { + msg->wait(); + } + return res; +} bool SurfaceFlinger::threadLoop() { waitForEvent(); + return true; +} - // check for transactions - if (CC_UNLIKELY(mConsoleSignals)) { - handleConsoleEvents(); - } +void SurfaceFlinger::onMessageReceived(int32_t what) +{ + switch (what) { + case MessageQueue::REFRESH: { +// case MessageQueue::INVALIDATE: { + // check for transactions + if (CC_UNLIKELY(mConsoleSignals)) { + handleConsoleEvents(); + } - // if we're in a global transaction, don't do anything. - const uint32_t mask = eTransactionNeeded | eTraversalNeeded; - uint32_t transactionFlags = peekTransactionFlags(mask); - if (CC_UNLIKELY(transactionFlags)) { - handleTransaction(transactionFlags); - } + // if we're in a global transaction, don't do anything. + const uint32_t mask = eTransactionNeeded | eTraversalNeeded; + uint32_t transactionFlags = peekTransactionFlags(mask); + if (CC_UNLIKELY(transactionFlags)) { + handleTransaction(transactionFlags); + } - // post surfaces (if needed) - handlePageFlip(); + // post surfaces (if needed) + handlePageFlip(); - if (mDirtyRegion.isEmpty()) { - // nothing new to do. - return true; - } +// signalRefresh(); +// +// } break; +// +// case MessageQueue::REFRESH: { - if (CC_UNLIKELY(mHwWorkListDirty)) { - // build the h/w work list - handleWorkList(); - } + handleRefresh(); - const DisplayHardware& hw(graphicPlane(0).displayHardware()); - if (CC_LIKELY(hw.canDraw())) { - // repaint the framebuffer (if needed) - handleRepaint(); - // inform the h/w that we're done compositing - hw.compositionComplete(); - postFramebuffer(); - } else { - // pretend we did the post - hw.compositionComplete(); + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + +// if (mDirtyRegion.isEmpty()) { +// return; +// } + + if (CC_UNLIKELY(mHwWorkListDirty)) { + // build the h/w work list + handleWorkList(); + } + + if (CC_LIKELY(hw.canDraw())) { + // repaint the framebuffer (if needed) + handleRepaint(); + // inform the h/w that we're done compositing + hw.compositionComplete(); + postFramebuffer(); + } else { + // pretend we did the post + hw.compositionComplete(); + } + + } break; } - return true; } void SurfaceFlinger::postFramebuffer() { - // this should never happen. we do the flip anyways so we don't - // risk to cause a deadlock with hwc - ALOGW_IF(mSwapRegion.isEmpty(), "mSwapRegion is empty"); + // mSwapRegion can be empty here is some cases, for instance if a hidden + // or fully transparent window is updating. + // in that case, we need to flip anyways to not risk a deadlock with + // h/w composer. + const DisplayHardware& hw(graphicPlane(0).displayHardware()); const nsecs_t now = systemTime(); mDebugInSwapBuffers = now; @@ -717,13 +738,13 @@ void SurfaceFlinger::commitTransaction() void SurfaceFlinger::handlePageFlip() { - bool visibleRegions = mVisibleRegionsDirty; + const DisplayHardware& hw = graphicPlane(0).displayHardware(); + const Region screenRegion(hw.bounds()); + const LayerVector& currentLayers(mDrawingState.layersSortedByZ); - visibleRegions |= lockPageFlip(currentLayers); + const bool visibleRegions = lockPageFlip(currentLayers); - const DisplayHardware& hw = graphicPlane(0).displayHardware(); - const Region screenRegion(hw.bounds()); - if (visibleRegions) { + if (visibleRegions || mVisibleRegionsDirty) { Region opaqueRegion; computeVisibleRegions(currentLayers, mDirtyRegion, opaqueRegion); @@ -770,7 +791,7 @@ void SurfaceFlinger::unlockPageFlip(const LayerVector& currentLayers) { const GraphicPlane& plane(graphicPlane(0)); const Transform& planeTransform(plane.transform()); - size_t count = currentLayers.size(); + const size_t count = currentLayers.size(); sp<LayerBase> const* layers = currentLayers.array(); for (size_t i=0 ; i<count ; i++) { const sp<LayerBase>& layer(layers[i]); @@ -778,6 +799,23 @@ void SurfaceFlinger::unlockPageFlip(const LayerVector& currentLayers) } } +void SurfaceFlinger::handleRefresh() +{ + bool needInvalidate = false; + const LayerVector& currentLayers(mDrawingState.layersSortedByZ); + const size_t count = currentLayers.size(); + for (size_t i=0 ; i<count ; i++) { + const sp<LayerBase>& layer(currentLayers[i]); + if (layer->onPreComposition()) { + needInvalidate = true; + } + } + if (needInvalidate) { + signalLayerUpdate(); + } +} + + void SurfaceFlinger::handleWorkList() { mHwWorkListDirty = false; @@ -1175,7 +1213,7 @@ uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags) { uint32_t old = android_atomic_or(flags, &mTransactionFlags); if ((old & flags)==0) { // wake the server up - signalEvent(); + signalTransaction(); } return old; } @@ -1426,14 +1464,14 @@ void SurfaceFlinger::screenReleased(int dpy) { // this may be called by a signal handler, we can't do too much in here android_atomic_or(eConsoleReleased, &mConsoleSignals); - signalEvent(); + signalTransaction(); } void SurfaceFlinger::screenAcquired(int dpy) { // this may be called by a signal handler, we can't do too much in here android_atomic_or(eConsoleAcquired, &mConsoleSignals); - signalEvent(); + signalTransaction(); } status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) @@ -1620,11 +1658,13 @@ void SurfaceFlinger::dumpAllLocked( snprintf(buffer, SIZE, " last eglSwapBuffers() time: %f us\n" " last transaction time : %f us\n" + " transaction-flags : %08x\n" " refresh-rate : %f fps\n" " x-dpi : %f\n" " y-dpi : %f\n", mLastSwapBufferTime/1000.0, mLastTransactionTime/1000.0, + mTransactionFlags, hw.getRefreshRate(), hw.getDpiX(), hw.getDpiY()); @@ -1753,6 +1793,7 @@ status_t SurfaceFlinger::onTransact( reply->writeInt32(0); reply->writeInt32(mDebugRegion); reply->writeInt32(mDebugBackground); + reply->writeInt32(mDebugDisableHWC); return NO_ERROR; case 1013: { Mutex::Autolock _l(mStateLock); @@ -1769,7 +1810,7 @@ void SurfaceFlinger::repaintEverything() { const DisplayHardware& hw(graphicPlane(0).displayHardware()); const Rect bounds(hw.getBounds()); setInvalidateRegion(Region(bounds)); - signalEvent(); + signalTransaction(); } void SurfaceFlinger::setInvalidateRegion(const Region& reg) { @@ -2245,7 +2286,7 @@ status_t SurfaceFlinger::turnElectronBeamOnImplLocked(int32_t mode) // make sure to redraw the whole screen when the animation is done mDirtyRegion.set(hw.bounds()); - signalEvent(); + signalTransaction(); return NO_ERROR; } diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index c24a9de45a..fcd93615ff 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -190,6 +190,8 @@ public: status_t renderScreenToTextureLocked(DisplayID dpy, GLuint* textureName, GLfloat* uOut, GLfloat* vOut); + void onMessageReceived(int32_t what); + status_t postMessageAsync(const sp<MessageBase>& msg, nsecs_t reltime=0, uint32_t flags = 0); @@ -283,7 +285,10 @@ private: public: // hack to work around gcc 4.0.3 bug const GraphicPlane& graphicPlane(int dpy) const; GraphicPlane& graphicPlane(int dpy); - void signalEvent(); + + void signalTransaction(); + void signalLayerUpdate(); + void signalRefresh(); void repaintEverything(); private: @@ -300,6 +305,7 @@ private: void handlePageFlip(); bool lockPageFlip(const LayerVector& currentLayers); void unlockPageFlip(const LayerVector& currentLayers); + void handleRefresh(); void handleWorkList(); void handleRepaint(); void postFramebuffer(); |