michael@0: /* michael@0: * Copyright (C) 2010 The Android Open Source Project michael@0: * Copyright (C) 2013 Mozilla Foundation michael@0: * michael@0: * Licensed under the Apache License, Version 2.0 (the "License"); michael@0: * you may not use this file except in compliance with the License. michael@0: * You may obtain a copy of the License at michael@0: * michael@0: * http://www.apache.org/licenses/LICENSE-2.0 michael@0: * michael@0: * Unless required by applicable law or agreed to in writing, software michael@0: * distributed under the License is distributed on an "AS IS" BASIS, michael@0: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. michael@0: * See the License for the specific language governing permissions and michael@0: * limitations under the License. michael@0: */ michael@0: michael@0: #define LOG_TAG "GonkConsumerBase" michael@0: #define ATRACE_TAG ATRACE_TAG_GRAPHICS michael@0: //#define LOG_NDEBUG 0 michael@0: michael@0: #define EGL_EGLEXT_PROTOTYPES michael@0: michael@0: #include michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "GonkConsumerBaseKK.h" michael@0: michael@0: // Macros for including the GonkConsumerBase name in log messages michael@0: #define CB_LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) michael@0: #define CB_LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) michael@0: #define CB_LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) michael@0: #define CB_LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__) michael@0: #define CB_LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) michael@0: michael@0: namespace android { michael@0: michael@0: // Get an ID that's unique within this process. michael@0: static int32_t createProcessUniqueId() { michael@0: static volatile int32_t globalCounter = 0; michael@0: return android_atomic_inc(&globalCounter); michael@0: } michael@0: michael@0: GonkConsumerBase::GonkConsumerBase(const sp& bufferQueue, bool controlledByApp) : michael@0: mAbandoned(false), michael@0: mConsumer(bufferQueue) { michael@0: // Choose a name using the PID and a process-unique ID. michael@0: mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId()); michael@0: michael@0: // Note that we can't create an sp<...>(this) in a ctor that will not keep a michael@0: // reference once the ctor ends, as that would cause the refcount of 'this' michael@0: // dropping to 0 at the end of the ctor. Since all we need is a wp<...> michael@0: // that's what we create. michael@0: wp listener = static_cast(this); michael@0: sp proxy = new GonkBufferQueue::ProxyConsumerListener(listener); michael@0: michael@0: status_t err = mConsumer->consumerConnect(proxy, controlledByApp); michael@0: if (err != NO_ERROR) { michael@0: CB_LOGE("GonkConsumerBase: error connecting to GonkBufferQueue: %s (%d)", michael@0: strerror(-err), err); michael@0: } else { michael@0: mConsumer->setConsumerName(mName); michael@0: } michael@0: } michael@0: michael@0: GonkConsumerBase::~GonkConsumerBase() { michael@0: CB_LOGV("~GonkConsumerBase"); michael@0: Mutex::Autolock lock(mMutex); michael@0: michael@0: // Verify that abandon() has been called before we get here. This should michael@0: // be done by GonkConsumerBase::onLastStrongRef(), but it's possible for a michael@0: // derived class to override that method and not call michael@0: // GonkConsumerBase::onLastStrongRef(). michael@0: LOG_ALWAYS_FATAL_IF(!mAbandoned, "[%s] ~GonkConsumerBase was called, but the " michael@0: "consumer is not abandoned!", mName.string()); michael@0: } michael@0: michael@0: void GonkConsumerBase::onLastStrongRef(const void* id) { michael@0: abandon(); michael@0: } michael@0: michael@0: void GonkConsumerBase::freeBufferLocked(int slotIndex) { michael@0: CB_LOGV("freeBufferLocked: slotIndex=%d", slotIndex); michael@0: mSlots[slotIndex].mGraphicBuffer = 0; michael@0: mSlots[slotIndex].mFence = Fence::NO_FENCE; michael@0: mSlots[slotIndex].mFrameNumber = 0; michael@0: } michael@0: michael@0: // Used for refactoring, should not be in final interface michael@0: sp GonkConsumerBase::getBufferQueue() const { michael@0: Mutex::Autolock lock(mMutex); michael@0: return mConsumer; michael@0: } michael@0: michael@0: void GonkConsumerBase::onFrameAvailable() { michael@0: CB_LOGV("onFrameAvailable"); michael@0: michael@0: sp listener; michael@0: { // scope for the lock michael@0: Mutex::Autolock lock(mMutex); michael@0: listener = mFrameAvailableListener.promote(); michael@0: } michael@0: michael@0: if (listener != NULL) { michael@0: CB_LOGV("actually calling onFrameAvailable"); michael@0: listener->onFrameAvailable(); michael@0: } michael@0: } michael@0: michael@0: void GonkConsumerBase::onBuffersReleased() { michael@0: Mutex::Autolock lock(mMutex); michael@0: michael@0: CB_LOGV("onBuffersReleased"); michael@0: michael@0: if (mAbandoned) { michael@0: // Nothing to do if we're already abandoned. michael@0: return; michael@0: } michael@0: michael@0: uint32_t mask = 0; michael@0: mConsumer->getReleasedBuffers(&mask); michael@0: for (int i = 0; i < GonkBufferQueue::NUM_BUFFER_SLOTS; i++) { michael@0: if (mask & (1 << i)) { michael@0: freeBufferLocked(i); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void GonkConsumerBase::abandon() { michael@0: CB_LOGV("abandon"); michael@0: Mutex::Autolock lock(mMutex); michael@0: michael@0: if (!mAbandoned) { michael@0: abandonLocked(); michael@0: mAbandoned = true; michael@0: } michael@0: } michael@0: michael@0: void GonkConsumerBase::abandonLocked() { michael@0: CB_LOGV("abandonLocked"); michael@0: for (int i =0; i < GonkBufferQueue::NUM_BUFFER_SLOTS; i++) { michael@0: freeBufferLocked(i); michael@0: } michael@0: // disconnect from the BufferQueue michael@0: mConsumer->consumerDisconnect(); michael@0: mConsumer.clear(); michael@0: } michael@0: michael@0: void GonkConsumerBase::setFrameAvailableListener( michael@0: const wp& listener) { michael@0: CB_LOGV("setFrameAvailableListener"); michael@0: Mutex::Autolock lock(mMutex); michael@0: mFrameAvailableListener = listener; michael@0: } michael@0: michael@0: void GonkConsumerBase::dump(String8& result) const { michael@0: dump(result, ""); michael@0: } michael@0: michael@0: void GonkConsumerBase::dump(String8& result, const char* prefix) const { michael@0: Mutex::Autolock _l(mMutex); michael@0: dumpLocked(result, prefix); michael@0: } michael@0: michael@0: void GonkConsumerBase::dumpLocked(String8& result, const char* prefix) const { michael@0: result.appendFormat("%smAbandoned=%d\n", prefix, int(mAbandoned)); michael@0: michael@0: if (!mAbandoned) { michael@0: mConsumer->dump(result, prefix); michael@0: } michael@0: } michael@0: michael@0: status_t GonkConsumerBase::acquireBufferLocked(IGonkGraphicBufferConsumer::BufferItem *item, michael@0: nsecs_t presentWhen) { michael@0: status_t err = mConsumer->acquireBuffer(item, presentWhen); michael@0: if (err != NO_ERROR) { michael@0: return err; michael@0: } michael@0: michael@0: if (item->mGraphicBuffer != NULL) { michael@0: mSlots[item->mBuf].mGraphicBuffer = item->mGraphicBuffer; michael@0: } michael@0: michael@0: mSlots[item->mBuf].mFrameNumber = item->mFrameNumber; michael@0: mSlots[item->mBuf].mFence = item->mFence; michael@0: michael@0: CB_LOGV("acquireBufferLocked: -> slot=%d", item->mBuf); michael@0: michael@0: return OK; michael@0: } michael@0: michael@0: status_t GonkConsumerBase::addReleaseFence(int slot, michael@0: const sp graphicBuffer, const sp& fence) { michael@0: Mutex::Autolock lock(mMutex); michael@0: return addReleaseFenceLocked(slot, graphicBuffer, fence); michael@0: } michael@0: michael@0: status_t GonkConsumerBase::addReleaseFenceLocked(int slot, michael@0: const sp graphicBuffer, const sp& fence) { michael@0: CB_LOGV("addReleaseFenceLocked: slot=%d", slot); michael@0: michael@0: // If consumer no longer tracks this graphicBuffer, we can safely michael@0: // drop this fence, as it will never be received by the producer. michael@0: if (!stillTracking(slot, graphicBuffer)) { michael@0: return OK; michael@0: } michael@0: michael@0: if (!mSlots[slot].mFence.get()) { michael@0: mSlots[slot].mFence = fence; michael@0: } else { michael@0: sp mergedFence = Fence::merge( michael@0: String8::format("%.28s:%d", mName.string(), slot), michael@0: mSlots[slot].mFence, fence); michael@0: if (!mergedFence.get()) { michael@0: CB_LOGE("failed to merge release fences"); michael@0: // synchronization is broken, the best we can do is hope fences michael@0: // signal in order so the new fence will act like a union michael@0: mSlots[slot].mFence = fence; michael@0: return BAD_VALUE; michael@0: } michael@0: mSlots[slot].mFence = mergedFence; michael@0: } michael@0: michael@0: return OK; michael@0: } michael@0: michael@0: status_t GonkConsumerBase::releaseBufferLocked(int slot, const sp graphicBuffer) { michael@0: // If consumer no longer tracks this graphicBuffer (we received a new michael@0: // buffer on the same slot), the buffer producer is definitely no longer michael@0: // tracking it. michael@0: if (!stillTracking(slot, graphicBuffer)) { michael@0: return OK; michael@0: } michael@0: michael@0: CB_LOGV("releaseBufferLocked: slot=%d/%llu", michael@0: slot, mSlots[slot].mFrameNumber); michael@0: status_t err = mConsumer->releaseBuffer(slot, mSlots[slot].mFrameNumber, mSlots[slot].mFence); michael@0: if (err == GonkBufferQueue::STALE_BUFFER_SLOT) { michael@0: freeBufferLocked(slot); michael@0: } michael@0: michael@0: mSlots[slot].mFence = Fence::NO_FENCE; michael@0: michael@0: return err; michael@0: } michael@0: michael@0: bool GonkConsumerBase::stillTracking(int slot, michael@0: const sp graphicBuffer) { michael@0: if (slot < 0 || slot >= GonkBufferQueue::NUM_BUFFER_SLOTS) { michael@0: return false; michael@0: } michael@0: return (mSlots[slot].mGraphicBuffer != NULL && michael@0: mSlots[slot].mGraphicBuffer->handle == graphicBuffer->handle); michael@0: } michael@0: michael@0: } // namespace android