diff --git a/include/gui/BufferQueue.h b/include/gui/BufferQueue.h index 0d36baa4f..85d8fb626 100644 --- a/include/gui/BufferQueue.h +++ b/include/gui/BufferQueue.h @@ -422,10 +422,6 @@ private: // 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; - // mMinUndequeuedBuffers is a constraint on the number of buffers // not dequeued at any time int mMinUndequeuedBuffers; diff --git a/include/gui/ConsumerBase.h b/include/gui/ConsumerBase.h new file mode 100644 index 000000000..d2bf0f649 --- /dev/null +++ b/include/gui/ConsumerBase.h @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_GUI_CONSUMERBASE_H +#define ANDROID_GUI_CONSUMERBASE_H + +#include + +#include + +#include +#include +#include + +namespace android { +// ---------------------------------------------------------------------------- + +class String8; + +// ConsumerBase is a base class for BufferQueue consumer end-points. It +// handles common tasks like management of the connection to the BufferQueue +// and the buffer pool. +class ConsumerBase : public virtual RefBase, + protected BufferQueue::ConsumerListener { +public: + struct FrameAvailableListener : public virtual RefBase { + // onFrameAvailable() is called 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; + }; + + virtual ~ConsumerBase(); + + // abandon frees all the buffers and puts the ConsumerBase into the + // 'abandoned' state. Once put in this state the ConsumerBase can never + // leave it. When in the 'abandoned' state, all methods of the + // ISurfaceTexture interface will fail with the NO_INIT error. + // + // Note that while calling this method causes all the buffers to be freed + // from the perspective of the the ConsumerBase, if there are additional + // references on the buffers (e.g. if a buffer is referenced by a client + // or by OpenGL ES as a texture) then those buffer will remain allocated. + void abandon(); + + // set the name of the ConsumerBase that will be used to identify it in + // log messages. + void setName(const String8& name); + + // getBufferQueue returns the BufferQueue object to which this + // ConsumerBase is connected. + sp getBufferQueue() const; + + // dump writes the current state to a string. These methods should NOT be + // overridden by child classes. Instead they should override the + // dumpLocked method, which is called by these methods after locking the + // mutex. + void dump(String8& result) const; + void dump(String8& result, const char* prefix, char* buffer, size_t SIZE) const; + + // setFrameAvailableListener sets the listener object that will be notified + // when a new frame becomes available. + void setFrameAvailableListener(const sp& listener); + +private: + ConsumerBase(const ConsumerBase&); + void operator=(const ConsumerBase&); + +protected: + + // TODO: Fix this comment + // ConsumerBase constructs a new ConsumerBase object. tex indicates the + // name of the OpenGL ES texture to which images are to be streamed. + // allowSynchronousMode specifies whether or not synchronous mode can be + // enabled. texTarget specifies the OpenGL ES texture target to which the + // texture will be bound in updateTexImage. useFenceSync specifies whether + // fences should be used to synchronize access to buffers if that behavior + // is enabled at compile-time. A custom bufferQueue can be specified + // if behavior for queue/dequeue/connect etc needs to be customized. + // Otherwise a default BufferQueue will be created and used. + // + // For legacy reasons, the ConsumerBase is created in a state where it is + // considered attached to an OpenGL ES context for the purposes of the + // attachToContext and detachFromContext methods. However, despite being + // considered "attached" to a context, the specific OpenGL ES context + // doesn't get latched until the first call to updateTexImage. After that + // point, all calls to updateTexImage must be made with the same OpenGL ES + // context current. + // + // A ConsumerBase may be detached from one OpenGL ES context and then + // attached to a different context using the detachFromContext and + // attachToContext methods, respectively. The intention of these methods is + // purely to allow a ConsumerBase to be transferred from one consumer + // context to another. If such a transfer is not needed there is no + // requirement that either of these methods be called. + ConsumerBase(const sp &bufferQueue); + + // Implementation of the BufferQueue::ConsumerListener interface. These + // calls are used to notify the ConsumerBase of asynchronous events in the + // BufferQueue. + virtual void onFrameAvailable(); + virtual void onBuffersReleased(); + + // freeBufferLocked frees up the given buffer slot. If the slot has been + // initialized this will release the reference to the GraphicBuffer in that + // slot and destroy the EGLImage in that slot. Otherwise it has no effect. + // + // This method must be called with mMutex locked. + virtual void freeBufferLocked(int slotIndex); + + // abandonLocked puts the BufferQueue into the abandoned state, causing + // all future operations on it to fail. This method rather than the public + // abandon method should be overridden by child classes to add abandon- + // time behavior. + // + // This method must be called with mMutex locked. + virtual void abandonLocked(); + + virtual void dumpLocked(String8& result, const char* prefix, char* buffer, + size_t SIZE) const; + + // acquireBufferLocked fetches the next buffer from the BufferQueue and + // updates the buffer slot for the buffer returned. + virtual status_t acquireBufferLocked(BufferQueue::BufferItem *item); + + // releaseBufferLocked relinquishes control over a buffer, returning that + // control to the BufferQueue. + virtual status_t releaseBufferLocked(int buf, EGLDisplay display, + EGLSyncKHR eglFence, const sp& fence); + + // Slot contains the information and object references that + // ConsumerBase maintains about a BufferQueue buffer slot. + struct Slot { + // mGraphicBuffer is the Gralloc buffer store in the slot or NULL if + // no Gralloc buffer is in the slot. + sp mGraphicBuffer; + + // mFence is a fence which will signal when the buffer associated with + // this buffer slot is no longer being used by the consumer and can be + // overwritten. The buffer can be dequeued before the fence signals; + // the producer is responsible for delaying writes until it signals. + sp mFence; + }; + + // mSlots stores the buffers that have been allocated by the BufferQueue + // for each buffer slot. It is initialized to null pointers, and gets + // filled in with the result of BufferQueue::acquire when the + // client dequeues a buffer from a + // slot that has not yet been used. The buffer allocated to a slot will also + // be replaced if the requested buffer usage or geometry differs from that + // of the buffer allocated to a slot. + Slot mSlots[BufferQueue::NUM_BUFFER_SLOTS]; + + // 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 IConsumerBase methods capable of returning an error. + bool mAbandoned; + + // mName is a string used to identify the ConsumerBase in log messages. + // It can be set by the setName method. + String8 mName; + + // 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 mFrameAvailableListener; + + // The ConsumerBase has-a BufferQueue and is responsible for creating this object + // if none is supplied + sp mBufferQueue; + + // mAttached indicates whether the ConsumerBase is currently attached to + // an OpenGL ES context. For legacy reasons, this is initialized to true, + // indicating that the ConsumerBase is considered to be attached to + // whatever context is current at the time of the first updateTexImage call. + // It is set to false by detachFromContext, and then set to true again by + // attachToContext. + bool mAttached; + + // mMutex is the mutex used to prevent concurrent access to the member + // variables of ConsumerBase objects. It must be locked whenever the + // member variables are accessed. + mutable Mutex mMutex; +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_GUI_CONSUMERBASE_H diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk index 7006957f1..3aa3a50f6 100644 --- a/libs/gui/Android.mk +++ b/libs/gui/Android.mk @@ -4,6 +4,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ BitTube.cpp \ BufferQueue.cpp \ + ConsumerBase.cpp \ DisplayEventReceiver.cpp \ IDisplayEventConnection.cpp \ ISensorEventConnection.cpp \ diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp index 23e3a4fa0..697635be3 100644 --- a/libs/gui/BufferQueue.cpp +++ b/libs/gui/BufferQueue.cpp @@ -85,7 +85,6 @@ BufferQueue::BufferQueue(bool allowSynchronousMode, int bufferCount, const sp& allocator) : mDefaultWidth(1), mDefaultHeight(1), - mPixelFormat(PIXEL_FORMAT_RGBA_8888), mMinUndequeuedBuffers(bufferCount), mMinAsyncBufferSlots(bufferCount + 1), mMinSyncBufferSlots(bufferCount), @@ -98,7 +97,7 @@ BufferQueue::BufferQueue(bool allowSynchronousMode, int bufferCount, mAbandoned(false), mFrameCounter(0), mBufferHasBeenQueued(false), - mDefaultBufferFormat(0), + mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888), mConsumerUsageBits(0), mTransformHint(0) { @@ -125,7 +124,8 @@ status_t BufferQueue::setBufferCountServerLocked(int bufferCount) { if (bufferCount > NUM_BUFFER_SLOTS) return BAD_VALUE; - // special-case, nothing to do + mServerBufferCount = bufferCount; + if (bufferCount == mBufferCount) return OK; @@ -133,7 +133,6 @@ status_t BufferQueue::setBufferCountServerLocked(int bufferCount) { bufferCount >= mBufferCount) { // easy, we just have more buffers mBufferCount = bufferCount; - mServerBufferCount = bufferCount; mDequeueCondition.broadcast(); } else { // we're here because we're either @@ -150,7 +149,6 @@ status_t BufferQueue::setBufferCountServerLocked(int bufferCount) { // own one. the actual resizing will happen during the next // dequeueBuffer. - mServerBufferCount = bufferCount; mDequeueCondition.broadcast(); } return OK; @@ -260,7 +258,7 @@ int BufferQueue::query(int what, int* outValue) value = mDefaultHeight; break; case NATIVE_WINDOW_FORMAT: - value = mPixelFormat; + value = mDefaultBufferFormat; break; case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS: value = mSynchronousMode ? @@ -447,12 +445,6 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp& outFence, 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; @@ -473,9 +465,6 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp& outFence, "failed"); return error; } - if (updateFormat) { - mPixelFormat = format; - } mSlots[buf].mAcquireCalled = false; mSlots[buf].mGraphicBuffer = graphicBuffer; @@ -791,9 +780,9 @@ void BufferQueue::dump(String8& result, const char* prefix, snprintf(buffer, SIZE, "%s-BufferQueue mBufferCount=%d, mSynchronousMode=%d, default-size=[%dx%d], " - "mPixelFormat=%d, FIFO(%d)={%s}\n", + "default-format=%d, FIFO(%d)={%s}\n", prefix, mBufferCount, mSynchronousMode, mDefaultWidth, - mDefaultHeight, mPixelFormat, fifoSize, fifo.string()); + mDefaultHeight, mDefaultBufferFormat, fifoSize, fifo.string()); result.append(buffer); @@ -835,21 +824,22 @@ void BufferQueue::dump(String8& result, const char* prefix, } } -void BufferQueue::freeBufferLocked(int i) { - mSlots[i].mGraphicBuffer = 0; - if (mSlots[i].mBufferState == BufferSlot::ACQUIRED) { - mSlots[i].mNeedsCleanupOnRelease = true; +void BufferQueue::freeBufferLocked(int slot) { + ST_LOGV("freeBufferLocked: slot=%d", slot); + mSlots[slot].mGraphicBuffer = 0; + if (mSlots[slot].mBufferState == BufferSlot::ACQUIRED) { + mSlots[slot].mNeedsCleanupOnRelease = true; } - mSlots[i].mBufferState = BufferSlot::FREE; - mSlots[i].mFrameNumber = 0; - mSlots[i].mAcquireCalled = false; + mSlots[slot].mBufferState = BufferSlot::FREE; + mSlots[slot].mFrameNumber = 0; + mSlots[slot].mAcquireCalled = false; // destroy fence as BufferQueue now takes ownership - if (mSlots[i].mEglFence != EGL_NO_SYNC_KHR) { - eglDestroySyncKHR(mSlots[i].mEglDisplay, mSlots[i].mEglFence); - mSlots[i].mEglFence = EGL_NO_SYNC_KHR; + if (mSlots[slot].mEglFence != EGL_NO_SYNC_KHR) { + eglDestroySyncKHR(mSlots[slot].mEglDisplay, mSlots[slot].mEglFence); + mSlots[slot].mEglFence = EGL_NO_SYNC_KHR; } - mSlots[i].mFence.clear(); + mSlots[slot].mFence.clear(); } void BufferQueue::freeAllBuffersLocked() { @@ -886,12 +876,14 @@ status_t BufferQueue::acquireBuffer(BufferItem *buffer) { buffer->mTimestamp = mSlots[buf].mTimestamp; buffer->mBuf = buf; buffer->mFence = mSlots[buf].mFence; - mSlots[buf].mAcquireCalled = true; + mSlots[buf].mAcquireCalled = true; + mSlots[buf].mNeedsCleanupOnRelease = false; mSlots[buf].mBufferState = BufferSlot::ACQUIRED; + mSlots[buf].mFence.clear(); + mQueue.erase(front); mDequeueCondition.broadcast(); - mSlots[buf].mFence.clear(); ATRACE_INT(mConsumerName.string(), mQueue.size()); } else { diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp new file mode 100644 index 000000000..af19ac081 --- /dev/null +++ b/libs/gui/ConsumerBase.cpp @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "ConsumerBase" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +//#define LOG_NDEBUG 0 + +#define GL_GLEXT_PROTOTYPES +#define EGL_EGLEXT_PROTOTYPES + +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include + +// Macros for including the ConsumerBase name in log messages +#define CB_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__) +#define CB_LOGD(x, ...) ALOGD("[%s] "x, mName.string(), ##__VA_ARGS__) +#define CB_LOGI(x, ...) ALOGI("[%s] "x, mName.string(), ##__VA_ARGS__) +#define CB_LOGW(x, ...) ALOGW("[%s] "x, mName.string(), ##__VA_ARGS__) +#define CB_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); +} + +ConsumerBase::ConsumerBase(const sp& bufferQueue) : + mBufferQueue(bufferQueue) { + // Choose a name using the PID and a process-unique ID. + mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId()); + + // Note that we can't create an sp<...>(this) in a ctor that will not keep a + // reference once the ctor ends, as that would cause the refcount of 'this' + // dropping to 0 at the end of the ctor. Since all we need is a wp<...> + // that's what we create. + wp listener; + sp proxy; + listener = static_cast(this); + proxy = new BufferQueue::ProxyConsumerListener(listener); + + status_t err = mBufferQueue->consumerConnect(proxy); + if (err != NO_ERROR) { + CB_LOGE("SurfaceTexture: error connecting to BufferQueue: %s (%d)", + strerror(-err), err); + } else { + mBufferQueue->setConsumerName(mName); + } +} + +ConsumerBase::~ConsumerBase() { + CB_LOGV("~ConsumerBase"); + abandon(); +} + +void ConsumerBase::freeBufferLocked(int slotIndex) { + CB_LOGV("freeBufferLocked: slotIndex=%d", slotIndex); + mSlots[slotIndex].mGraphicBuffer = 0; + mSlots[slotIndex].mFence = 0; +} + +// Used for refactoring, should not be in final interface +sp ConsumerBase::getBufferQueue() const { + Mutex::Autolock lock(mMutex); + return mBufferQueue; +} + +void ConsumerBase::onFrameAvailable() { + CB_LOGV("onFrameAvailable"); + + sp listener; + { // scope for the lock + Mutex::Autolock lock(mMutex); + listener = mFrameAvailableListener; + } + + if (listener != NULL) { + CB_LOGV("actually calling onFrameAvailable"); + listener->onFrameAvailable(); + } +} + +void ConsumerBase::onBuffersReleased() { + Mutex::Autolock lock(mMutex); + + CB_LOGV("onBuffersReleased"); + + if (mAbandoned) { + // Nothing to do if we're already abandoned. + return; + } + + uint32_t mask = 0; + mBufferQueue->getReleasedBuffers(&mask); + for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { + if (mask & (1 << i)) { + freeBufferLocked(i); + } + } +} + +void ConsumerBase::abandon() { + CB_LOGV("abandon"); + Mutex::Autolock lock(mMutex); + + if (!mAbandoned) { + abandonLocked(); + mAbandoned = true; + } +} + +void ConsumerBase::abandonLocked() { + CB_LOGV("abandonLocked"); + for (int i =0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { + freeBufferLocked(i); + } + // disconnect from the BufferQueue + mBufferQueue->consumerDisconnect(); + mBufferQueue.clear(); +} + +void ConsumerBase::setFrameAvailableListener( + const sp& listener) { + CB_LOGV("setFrameAvailableListener"); + Mutex::Autolock lock(mMutex); + mFrameAvailableListener = listener; +} + +void ConsumerBase::dump(String8& result) const { + char buffer[1024]; + dump(result, "", buffer, 1024); +} + +void ConsumerBase::dump(String8& result, const char* prefix, + char* buffer, size_t size) const { + Mutex::Autolock _l(mMutex); + dumpLocked(result, prefix, buffer, size); +} + +void ConsumerBase::dumpLocked(String8& result, const char* prefix, + char* buffer, size_t SIZE) const { + snprintf(buffer, SIZE, "%smAbandoned=%d\n", prefix, int(mAbandoned)); + result.append(buffer); + + if (!mAbandoned) { + mBufferQueue->dump(result, prefix, buffer, SIZE); + } +} + +status_t ConsumerBase::acquireBufferLocked(BufferQueue::BufferItem *item) { + status_t err = mBufferQueue->acquireBuffer(item); + if (err != NO_ERROR) { + return err; + } + + if (item->mGraphicBuffer != NULL) { + mSlots[item->mBuf].mGraphicBuffer = item->mGraphicBuffer; + } + + CB_LOGV("acquireBufferLocked: -> slot=%d", item->mBuf); + + return OK; +} + +status_t ConsumerBase::releaseBufferLocked(int slot, EGLDisplay display, + EGLSyncKHR eglFence, const sp& fence) { + CB_LOGV("releaseBufferLocked: slot=%d", slot); + status_t err = mBufferQueue->releaseBuffer(slot, display, eglFence, fence); + if (err == BufferQueue::STALE_BUFFER_SLOT) { + freeBufferLocked(slot); + } + + mSlots[slot].mFence.clear(); + + return err; +} + +} // namespace android \ No newline at end of file diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp index 68b9feb07..a2aca8f0e 100644 --- a/libs/gui/SurfaceTexture.cpp +++ b/libs/gui/SurfaceTexture.cpp @@ -316,7 +316,7 @@ status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter) { computeCurrentTransformMatrix(); } else { if (err < 0) { - ALOGE("updateTexImage failed on acquire %d", err); + ST_LOGE("updateTexImage failed on acquire %d", err); } // We always bind the texture even if we don't update its contents. glBindTexture(mTexTarget, mTexName); @@ -327,7 +327,7 @@ status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter) { } void SurfaceTexture::setReleaseFence(int fenceFd) { - if (fenceFd == -1) + if (fenceFd == -1 || mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) return; sp fence(new Fence(fenceFd)); if (!mEGLSlots[mCurrentTexture].mReleaseFence.get()) { @@ -337,7 +337,7 @@ void SurfaceTexture::setReleaseFence(int fenceFd) { String8("SurfaceTexture merged release"), mEGLSlots[mCurrentTexture].mReleaseFence, fence); if (!mergedFence.get()) { - ALOGE("failed to merge release fences"); + ST_LOGE("failed to merge release fences"); // synchronization is broken, the best we can do is hope fences // signal in order so the new fence will act like a union mEGLSlots[mCurrentTexture].mReleaseFence = fence; diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index 175f80852..56ac63578 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -27,6 +27,8 @@ #include #include +#include + #include #include #include @@ -100,11 +102,13 @@ void checkEGLErrors(const char* token) DisplayDevice::DisplayDevice( const sp& flinger, int display, - const sp& surface, + const sp& nativeWindow, + const sp& framebufferSurface, EGLConfig config) : mFlinger(flinger), mId(display), - mNativeWindow(surface), + mNativeWindow(nativeWindow), + mFramebufferSurface(framebufferSurface), mDisplay(EGL_NO_DISPLAY), mSurface(EGL_NO_SURFACE), mContext(EGL_NO_CONTEXT), @@ -164,12 +168,6 @@ void DisplayDevice::init(EGLConfig config) { ANativeWindow* const window = mNativeWindow.get(); - int concreteType; - window->query(window, NATIVE_WINDOW_CONCRETE_TYPE, &concreteType); - if (concreteType == NATIVE_WINDOW_FRAMEBUFFER) { - mFramebufferSurface = static_cast(mNativeWindow.get()); - } - int format; window->query(window, NATIVE_WINDOW_FORMAT, &format); mDpiX = window->xdpi; @@ -216,16 +214,6 @@ void DisplayDevice::init(EGLConfig config) eglQuerySurface(display, surface, EGL_WIDTH, &mDisplayWidth); eglQuerySurface(display, surface, EGL_HEIGHT, &mDisplayHeight); - if (mFramebufferSurface != NULL) { - if (mFramebufferSurface->isUpdateOnDemand()) { - mFlags |= PARTIAL_UPDATES; - // if we have partial updates, we definitely don't need to - // preserve the backbuffer, which may be costly. - eglSurfaceAttrib(display, surface, - EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED); - } - } - mDisplay = display; mSurface = surface; mFormat = format; @@ -262,12 +250,6 @@ void DisplayDevice::flip(const Region& dirty) const } #endif - if (mFlags & PARTIAL_UPDATES) { - if (mFramebufferSurface != NULL) { - mFramebufferSurface->setUpdateRectangle(dirty.getBounds()); - } - } - mPageFlipCount++; } diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index b34bcf175..78d44f384 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -62,7 +62,8 @@ public: DisplayDevice( const sp& flinger, int dpy, - const sp& surface, + const sp& nativeWindow, + const sp& framebufferSurface, EGLConfig config); ~DisplayDevice(); diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp index 7695e7fd0..f32913651 100644 --- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp +++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp @@ -1,6 +1,6 @@ /* ** - ** Copyright 2007 The Android Open Source Project + ** Copyright 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. @@ -48,17 +48,33 @@ sp FramebufferSurface::create() { // ---------------------------------------------------------------------------- +class GraphicBufferAlloc : public BnGraphicBufferAlloc { +public: + GraphicBufferAlloc() { }; + virtual ~GraphicBufferAlloc() { }; + virtual sp createGraphicBuffer(uint32_t w, uint32_t h, + PixelFormat format, uint32_t usage, status_t* error) { + sp graphicBuffer(new GraphicBuffer(w, h, format, usage)); + return graphicBuffer; + } +}; + + /* * This implements the (main) framebuffer management. This class is used * mostly by SurfaceFlinger, but also by command line GL application. * */ -FramebufferSurface::FramebufferSurface() - : SurfaceTextureClient(), - fbDev(0), mCurrentBufferIndex(-1), mUpdateOnDemand(false) +FramebufferSurface::FramebufferSurface(): + ConsumerBase(new BufferQueue(true, NUM_FRAME_BUFFERS, + new GraphicBufferAlloc())), + fbDev(0), + mCurrentBufferSlot(-1), + mCurrentBuffer(0) { hw_module_t const* module; + if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module) == 0) { int stride; int err; @@ -70,85 +86,64 @@ FramebufferSurface::FramebufferSurface() if (!fbDev) return; - mUpdateOnDemand = (fbDev->setUpdateRect != 0); - - const_cast(ANativeWindow::flags) = fbDev->flags; - const_cast(ANativeWindow::minSwapInterval) = fbDev->minSwapInterval; - const_cast(ANativeWindow::maxSwapInterval) = fbDev->maxSwapInterval; - - if (fbDev->xdpi == 0 || fbDev->ydpi == 0) { - ALOGE("invalid screen resolution from fb HAL (xdpi=%f, ydpi=%f), " - "defaulting to 160 dpi", fbDev->xdpi, fbDev->ydpi); - const_cast(ANativeWindow::xdpi) = 160; - const_cast(ANativeWindow::ydpi) = 160; - } else { - const_cast(ANativeWindow::xdpi) = fbDev->xdpi; - const_cast(ANativeWindow::ydpi) = fbDev->ydpi; - } - + mName = "FramebufferSurface"; + mBufferQueue->setConsumerName(mName); + mBufferQueue->setConsumerUsageBits(GRALLOC_USAGE_HW_FB | + GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER); + mBufferQueue->setDefaultBufferFormat(fbDev->format); + mBufferQueue->setDefaultBufferSize(fbDev->width, fbDev->height); + mBufferQueue->setSynchronousMode(true); + mBufferQueue->setBufferCountServer(NUM_FRAME_BUFFERS); } else { ALOGE("Couldn't get gralloc module"); } - - class GraphicBufferAlloc : public BnGraphicBufferAlloc { - public: - GraphicBufferAlloc() { }; - virtual ~GraphicBufferAlloc() { }; - virtual sp createGraphicBuffer(uint32_t w, uint32_t h, - PixelFormat format, uint32_t usage, status_t* error) { - sp graphicBuffer(new GraphicBuffer(w, h, format, usage)); - return graphicBuffer; - } - }; - - mBufferQueue = new BufferQueue(true, NUM_FRAME_BUFFERS, new GraphicBufferAlloc()); - mBufferQueue->setConsumerUsageBits(GRALLOC_USAGE_HW_FB|GRALLOC_USAGE_HW_RENDER|GRALLOC_USAGE_HW_COMPOSER); - mBufferQueue->setDefaultBufferFormat(fbDev->format); - mBufferQueue->setDefaultBufferSize(fbDev->width, fbDev->height); - mBufferQueue->setSynchronousMode(true); - mBufferQueue->setBufferCountServer(NUM_FRAME_BUFFERS); - setISurfaceTexture(mBufferQueue); } -void FramebufferSurface::onFirstRef() { - class Listener : public BufferQueue::ConsumerListener { - const wp that; - virtual ~Listener() { } - virtual void onBuffersReleased() { } - void onFrameAvailable() { - sp self = that.promote(); - if (self != NULL) { - BufferQueue::BufferItem item; - status_t err = self->mBufferQueue->acquireBuffer(&item); - if (err == 0) { - if (item.mGraphicBuffer != 0) { - self->mBuffers[item.mBuf] = item.mGraphicBuffer; - } - if (item.mFence.get()) { - err = item.mFence->wait(Fence::TIMEOUT_NEVER); - if (err) { - ALOGE("failed waiting for buffer's fence: %d", err); - self->mBufferQueue->releaseBuffer(item.mBuf, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, - item.mFence); - return; - } - } - self->fbDev->post(self->fbDev, self->mBuffers[item.mBuf]->handle); - if (self->mCurrentBufferIndex >= 0) { - self->mBufferQueue->releaseBuffer(self->mCurrentBufferIndex, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); - } - self->mCurrentBufferIndex = item.mBuf; - } - } - } - public: - Listener(const sp& that) : that(that) { } - }; +status_t FramebufferSurface::nextBuffer(sp* buffer) { + Mutex::Autolock lock(mMutex); - mBufferQueue->setConsumerName(String8("FramebufferSurface")); - mBufferQueue->consumerConnect(new Listener(this)); + BufferQueue::BufferItem item; + status_t err = acquireBufferLocked(&item); + if (err == BufferQueue::NO_BUFFER_AVAILABLE) { + if (buffer != NULL) { + *buffer = mCurrentBuffer; + } + return NO_ERROR; + } else if (err != NO_ERROR) { + ALOGE("error acquiring buffer: %s (%d)", strerror(-err), err); + return err; + } + + // If the BufferQueue has freed and reallocated a buffer in mCurrentSlot + // then we may have acquired the slot we already own. If we had released + // our current buffer before we call acquireBuffer then that release call + // would have returned STALE_BUFFER_SLOT, and we would have called + // freeBufferLocked on that slot. Because the buffer slot has already + // been overwritten with the new buffer all we have to do is skip the + // releaseBuffer call and we should be in the same state we'd be in if we + // had released the old buffer first. + if (mCurrentBufferSlot != BufferQueue::INVALID_BUFFER_SLOT && + item.mBuf != mCurrentBufferSlot) { + // Release the previous buffer. + err = releaseBufferLocked(mCurrentBufferSlot, EGL_NO_DISPLAY, + EGL_NO_SYNC_KHR, Fence::NO_FENCE); + if (err != NO_ERROR && err != BufferQueue::STALE_BUFFER_SLOT) { + ALOGE("error releasing buffer: %s (%d)", strerror(-err), err); + return err; + } + } + + mCurrentBufferSlot = item.mBuf; + mCurrentBuffer = mSlots[mCurrentBufferSlot].mGraphicBuffer; + if (item.mFence != NULL) { + item.mFence->wait(Fence::TIMEOUT_NEVER); + } + + if (buffer != NULL) { + *buffer = mCurrentBuffer; + } + + return NO_ERROR; } FramebufferSurface::~FramebufferSurface() { @@ -157,6 +152,29 @@ FramebufferSurface::~FramebufferSurface() { } } +void FramebufferSurface::onFrameAvailable() { + // XXX: The following code is here temporarily as part of the transition + // away from the framebuffer HAL. + sp buf; + status_t err = nextBuffer(&buf); + if (err != NO_ERROR) { + ALOGE("error latching next FramebufferSurface buffer: %s (%d)", + strerror(-err), err); + return; + } + err = fbDev->post(fbDev, buf->handle); + if (err != NO_ERROR) { + ALOGE("error posting framebuffer: %d", err); + } +} + +void FramebufferSurface::freeBufferLocked(int slotIndex) { + ConsumerBase::freeBufferLocked(slotIndex); + if (slotIndex == mCurrentBufferSlot) { + mCurrentBufferSlot = BufferQueue::INVALID_BUFFER_SLOT; + } +} + float FramebufferSurface::getRefreshRate() const { /* FIXME: REFRESH_RATE is a temporary HACK until we are able to report the * refresh rate properly from the HAL. The WindowManagerService now relies @@ -172,10 +190,7 @@ float FramebufferSurface::getRefreshRate() const { status_t FramebufferSurface::setUpdateRectangle(const Rect& r) { - if (!mUpdateOnDemand) { - return INVALID_OPERATION; - } - return fbDev->setUpdateRect(fbDev, r.left, r.top, r.width(), r.height()); + return INVALID_OPERATION; } status_t FramebufferSurface::compositionComplete() @@ -190,38 +205,10 @@ void FramebufferSurface::dump(String8& result) { if (fbDev->common.version >= 1 && fbDev->dump) { const size_t SIZE = 4096; char buffer[SIZE]; - fbDev->dump(fbDev, buffer, SIZE); result.append(buffer); } -} - -int FramebufferSurface::query(int what, int* value) const { - Mutex::Autolock _l(mLock); - framebuffer_device_t* fb = fbDev; - switch (what) { - case NATIVE_WINDOW_DEFAULT_WIDTH: - case NATIVE_WINDOW_WIDTH: - *value = fb->width; - return NO_ERROR; - case NATIVE_WINDOW_DEFAULT_HEIGHT: - case NATIVE_WINDOW_HEIGHT: - *value = fb->height; - return NO_ERROR; - case NATIVE_WINDOW_FORMAT: - *value = fb->format; - return NO_ERROR; - case NATIVE_WINDOW_CONCRETE_TYPE: - *value = NATIVE_WINDOW_FRAMEBUFFER; - return NO_ERROR; - case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: - *value = 0; - return NO_ERROR; - case NATIVE_WINDOW_TRANSFORM_HINT: - *value = 0; - return NO_ERROR; - } - return SurfaceTextureClient::query(what, value); + ConsumerBase::dump(result); } // ---------------------------------------------------------------------------- diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h index 672bfbb7d..95feaa05d 100644 --- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h +++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h @@ -20,9 +20,7 @@ #include #include -#include - -#include +#include #define NUM_FRAME_BUFFERS 2 @@ -35,7 +33,7 @@ class String8; // --------------------------------------------------------------------------- -class FramebufferSurface : public SurfaceTextureClient { +class FramebufferSurface : public ConsumerBase { public: static sp create(); @@ -43,28 +41,34 @@ public: // TODO: this should be coming from HWC float getRefreshRate() const; - bool isUpdateOnDemand() const { return mUpdateOnDemand; } + bool isUpdateOnDemand() const { return false; } status_t setUpdateRectangle(const Rect& updateRect); status_t compositionComplete(); - void dump(String8& result); + virtual void dump(String8& result); -protected: - virtual void onFirstRef(); + // nextBuffer waits for and then latches the next buffer from the + // BufferQueue and releases the previously latched buffer to the + // BufferQueue. The new buffer is returned in the 'buffer' argument. + status_t nextBuffer(sp* buffer); private: FramebufferSurface(); virtual ~FramebufferSurface(); // this class cannot be overloaded - virtual int query(int what, int* value) const; + + virtual void onFrameAvailable(); + virtual void freeBufferLocked(int slotIndex); framebuffer_device_t* fbDev; - sp mBufferQueue; - int mCurrentBufferIndex; - sp mBuffers[NUM_FRAME_BUFFERS]; + // mCurrentBufferIndex is the slot index of the current buffer or + // INVALID_BUFFER_SLOT to indicate that either there is no current buffer + // or the buffer is not associated with a slot. + int mCurrentBufferSlot; - mutable Mutex mLock; - bool mUpdateOnDemand; + // mCurrentBuffer is the current buffer or NULL to indicate that there is + // no current buffer. + sp mCurrentBuffer; }; // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index b9c93371e..607cbae8f 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -42,6 +42,7 @@ namespace android { class GraphicBuffer; class LayerBase; +class Region; class String8; class SurfaceFlinger; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 1269f3b2e..1f9d69493 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -34,8 +34,9 @@ #include -#include #include +#include +#include #include #include @@ -351,22 +352,24 @@ status_t SurfaceFlinger::readyToRun() // Initialize the main display // create native window to main display - sp anw = FramebufferSurface::create(); - ANativeWindow* const window = anw.get(); - if (!window) { + sp fbs = FramebufferSurface::create(); + if (fbs == NULL) { ALOGE("Display subsystem failed to initialize. check logs. exiting..."); exit(0); } + sp stc(new SurfaceTextureClient(static_cast >(fbs->getBufferQueue()))); + // initialize the config and context int format; - window->query(window, NATIVE_WINDOW_FORMAT, &format); + ANativeWindow* const anw = stc.get(); + anw->query(anw, NATIVE_WINDOW_FORMAT, &format); mEGLConfig = selectEGLConfig(mEGLDisplay, format); mEGLContext = createGLContext(mEGLDisplay, mEGLConfig); // initialize our main display hardware mCurrentState.displays.add(DisplayDevice::DISPLAY_ID_MAIN, DisplayDeviceState()); - sp hw = new DisplayDevice(this, DisplayDevice::DISPLAY_ID_MAIN, anw, mEGLConfig); + sp hw = new DisplayDevice(this, DisplayDevice::DISPLAY_ID_MAIN, anw, fbs, mEGLConfig); mDisplays.add(DisplayDevice::DISPLAY_ID_MAIN, hw); // initialize OpenGL ES @@ -868,7 +871,7 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) for (size_t i=0 ; i disp = new DisplayDevice(this, curr[i].id, 0, mEGLConfig); + sp disp = new DisplayDevice(this, curr[i].id, 0, 0, mEGLConfig); mDisplays.add(curr[i].id, disp); } }