summaryrefslogtreecommitdiffstats
path: root/widget/gonk/nativewindow
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /widget/gonk/nativewindow
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'widget/gonk/nativewindow')
-rw-r--r--widget/gonk/nativewindow/FakeSurfaceComposer.cpp703
-rw-r--r--widget/gonk/nativewindow/FakeSurfaceComposer.h175
-rw-r--r--widget/gonk/nativewindow/GonkBufferQueue.h22
-rw-r--r--widget/gonk/nativewindow/GonkBufferQueueJB.cpp1036
-rw-r--r--widget/gonk/nativewindow/GonkBufferQueueJB.h653
-rw-r--r--widget/gonk/nativewindow/GonkBufferQueueKK.cpp1265
-rw-r--r--widget/gonk/nativewindow/GonkBufferQueueKK.h583
-rw-r--r--widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferItem.cpp193
-rw-r--r--widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferItem.h101
-rw-r--r--widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueConsumer.cpp559
-rw-r--r--widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueConsumer.h173
-rw-r--r--widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueCore.cpp243
-rw-r--r--widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueCore.h251
-rw-r--r--widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueDefs.h36
-rw-r--r--widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueLL.cpp96
-rw-r--r--widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueLL.h94
-rw-r--r--widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueProducer.cpp886
-rw-r--r--widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueProducer.h216
-rw-r--r--widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferSlot.cpp32
-rw-r--r--widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferSlot.h132
-rw-r--r--widget/gonk/nativewindow/GonkConsumerBaseJB.cpp245
-rw-r--r--widget/gonk/nativewindow/GonkConsumerBaseJB.h239
-rw-r--r--widget/gonk/nativewindow/GonkConsumerBaseKK.cpp252
-rw-r--r--widget/gonk/nativewindow/GonkConsumerBaseKK.h240
-rw-r--r--widget/gonk/nativewindow/GonkConsumerBaseLL.cpp257
-rw-r--r--widget/gonk/nativewindow/GonkConsumerBaseLL.h245
-rw-r--r--widget/gonk/nativewindow/GonkNativeWindow.h22
-rw-r--r--widget/gonk/nativewindow/GonkNativeWindowJB.cpp180
-rw-r--r--widget/gonk/nativewindow/GonkNativeWindowJB.h133
-rw-r--r--widget/gonk/nativewindow/GonkNativeWindowKK.cpp182
-rw-r--r--widget/gonk/nativewindow/GonkNativeWindowKK.h135
-rw-r--r--widget/gonk/nativewindow/GonkNativeWindowLL.cpp204
-rw-r--r--widget/gonk/nativewindow/GonkNativeWindowLL.h133
-rw-r--r--widget/gonk/nativewindow/IGonkGraphicBufferConsumer.h20
-rw-r--r--widget/gonk/nativewindow/IGonkGraphicBufferConsumerKK.cpp480
-rw-r--r--widget/gonk/nativewindow/IGonkGraphicBufferConsumerKK.h228
-rw-r--r--widget/gonk/nativewindow/IGonkGraphicBufferConsumerLL.cpp565
-rw-r--r--widget/gonk/nativewindow/IGonkGraphicBufferConsumerLL.h337
-rw-r--r--widget/gonk/nativewindow/moz.build104
39 files changed, 11650 insertions, 0 deletions
diff --git a/widget/gonk/nativewindow/FakeSurfaceComposer.cpp b/widget/gonk/nativewindow/FakeSurfaceComposer.cpp
new file mode 100644
index 000000000..7e4a2a9d8
--- /dev/null
+++ b/widget/gonk/nativewindow/FakeSurfaceComposer.cpp
@@ -0,0 +1,703 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ * Copyright (C) 2013 Mozilla Foundation
+ *
+ * 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 "FakeSurfaceComposer"
+//#define LOG_NDEBUG 0
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <cutils/atomic.h>
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <private/android_filesystem_config.h>
+
+#include <gui/IDisplayEventConnection.h>
+#include <gui/GraphicBufferAlloc.h>
+#include <gui/Surface.h>
+#include <ui/DisplayInfo.h>
+
+#if ANDROID_VERSION >= 21
+#include <ui/Rect.h>
+#endif
+
+#include "../libdisplay/GonkDisplay.h"
+#include "../nsScreenManagerGonk.h"
+#include "FakeSurfaceComposer.h"
+#include "gfxPrefs.h"
+#include "MainThreadUtils.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "nsProxyRelease.h"
+#include "nsThreadUtils.h"
+
+using namespace mozilla;
+
+namespace android {
+
+/* static */
+void FakeSurfaceComposer::instantiate() {
+ defaultServiceManager()->addService(
+ String16("SurfaceFlinger"), new FakeSurfaceComposer());
+}
+
+FakeSurfaceComposer::FakeSurfaceComposer()
+ : BnSurfaceComposer()
+{
+}
+
+FakeSurfaceComposer::~FakeSurfaceComposer()
+{
+}
+
+status_t FakeSurfaceComposer::onTransact(uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags)
+{
+ switch (code) {
+ case CREATE_CONNECTION:
+ case CREATE_DISPLAY:
+ case SET_TRANSACTION_STATE:
+ case CAPTURE_SCREEN:
+ {
+ // codes that require permission check
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int pid = ipc->getCallingPid();
+ const int uid = ipc->getCallingUid();
+ // Accept request only when uid is root.
+ if (uid != AID_ROOT) {
+ ALOGE("Permission Denial: "
+ "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid);
+ return PERMISSION_DENIED;
+ }
+ break;
+ }
+ }
+
+ return BnSurfaceComposer::onTransact(code, data, reply, flags);
+}
+
+sp<ISurfaceComposerClient> FakeSurfaceComposer::createConnection()
+{
+ return nullptr;
+}
+
+sp<IGraphicBufferAlloc> FakeSurfaceComposer::createGraphicBufferAlloc()
+{
+ sp<GraphicBufferAlloc> gba(new GraphicBufferAlloc());
+ return gba;
+}
+
+class DestroyDisplayRunnable : public Runnable {
+public:
+ DestroyDisplayRunnable(FakeSurfaceComposer* aComposer, ssize_t aIndex)
+ : mComposer(aComposer), mIndex(aIndex) { }
+ NS_IMETHOD Run() override {
+ MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
+ Mutex::Autolock _l(mComposer->mStateLock);
+ RefPtr<nsScreenManagerGonk> screenManager =
+ nsScreenManagerGonk::GetInstance();
+ screenManager->RemoveScreen(GonkDisplay::DISPLAY_VIRTUAL);
+ mComposer->mDisplays.removeItemsAt(mIndex);
+ return NS_OK;
+ }
+ sp<FakeSurfaceComposer> mComposer;
+ ssize_t mIndex;
+};
+
+sp<IBinder> FakeSurfaceComposer::createDisplay(const String8& displayName,
+ bool secure)
+{
+#if ANDROID_VERSION >= 19
+ class DisplayToken : public BBinder {
+ sp<FakeSurfaceComposer> composer;
+ virtual ~DisplayToken() {
+ MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
+ // no more references, this display must be terminated
+ Mutex::Autolock _l(composer->mStateLock);
+ ssize_t idx = composer->mDisplays.indexOfKey(this);
+ if (idx >= 0) {
+ nsCOMPtr<nsIRunnable> task(new DestroyDisplayRunnable(composer.get(), idx));
+ NS_DispatchToMainThread(task);
+ }
+ }
+ public:
+ DisplayToken(const sp<FakeSurfaceComposer>& composer)
+ : composer(composer) {
+ }
+ };
+
+ sp<BBinder> token = new DisplayToken(this);
+
+ Mutex::Autolock _l(mStateLock);
+ DisplayDeviceState info(HWC_DISPLAY_VIRTUAL);
+ info.displayName = displayName;
+ info.displayId = GonkDisplay::DISPLAY_VIRTUAL;
+ info.isSecure = secure;
+ mDisplays.add(token, info);
+ return token;
+#else
+ return nullptr;
+#endif
+}
+
+#if ANDROID_VERSION >= 19
+void FakeSurfaceComposer::destroyDisplay(const sp<IBinder>& display)
+{
+ Mutex::Autolock _l(mStateLock);
+
+ ssize_t idx = mDisplays.indexOfKey(display);
+ if (idx < 0) {
+ ALOGW("destroyDisplay: invalid display token");
+ return;
+ }
+
+ nsCOMPtr<nsIRunnable> task(new DestroyDisplayRunnable(this, idx));
+ NS_DispatchToMainThread(task);
+}
+#endif
+
+sp<IBinder> FakeSurfaceComposer::getBuiltInDisplay(int32_t id)
+{
+ // support only primary display
+ if (uint32_t(id) != HWC_DISPLAY_PRIMARY) {
+ return NULL;
+ }
+
+ if (!mPrimaryDisplay.get()) {
+ mPrimaryDisplay = new BBinder();
+ }
+ return mPrimaryDisplay;
+}
+
+void FakeSurfaceComposer::setTransactionState(
+ const Vector<ComposerState>& state,
+ const Vector<DisplayState>& displays,
+ uint32_t flags)
+{
+ Mutex::Autolock _l(mStateLock);
+ size_t count = displays.size();
+ for (size_t i=0 ; i<count ; i++) {
+ const DisplayState& s(displays[i]);
+ setDisplayStateLocked(s);
+ }
+}
+
+uint32_t FakeSurfaceComposer::setDisplayStateLocked(const DisplayState& s)
+{
+ ssize_t dpyIdx = mDisplays.indexOfKey(s.token);
+ if (dpyIdx < 0) {
+ return 0;
+ }
+
+ uint32_t flags = 0;
+ DisplayDeviceState& disp(mDisplays.editValueAt(dpyIdx));
+
+ if (!disp.isValid()) {
+ return 0;
+ }
+
+ const uint32_t what = s.what;
+ if (what & DisplayState::eSurfaceChanged) {
+ if (disp.surface->asBinder() != s.surface->asBinder()) {
+ disp.surface = s.surface;
+ flags |= eDisplayTransactionNeeded;
+ }
+ }
+ if (what & DisplayState::eLayerStackChanged) {
+ if (disp.layerStack != s.layerStack) {
+ disp.layerStack = s.layerStack;
+ flags |= eDisplayTransactionNeeded;
+ }
+ }
+ if (what & DisplayState::eDisplayProjectionChanged) {
+ if (disp.orientation != s.orientation) {
+ disp.orientation = s.orientation;
+ flags |= eDisplayTransactionNeeded;
+ }
+ if (disp.frame != s.frame) {
+ disp.frame = s.frame;
+ flags |= eDisplayTransactionNeeded;
+ }
+ if (disp.viewport != s.viewport) {
+ disp.viewport = s.viewport;
+ flags |= eDisplayTransactionNeeded;
+ }
+ }
+#if ANDROID_VERSION >= 21
+ if (what & DisplayState::eDisplaySizeChanged) {
+ if (disp.width != s.width) {
+ disp.width = s.width;
+ flags |= eDisplayTransactionNeeded;
+ }
+ if (disp.height != s.height) {
+ disp.height = s.height;
+ flags |= eDisplayTransactionNeeded;
+ }
+ }
+#endif
+
+ if (what & DisplayState::eSurfaceChanged) {
+ nsCOMPtr<nsIRunnable> runnable =
+ NS_NewRunnableFunction([&]() {
+ MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
+ RefPtr<nsScreenManagerGonk> screenManager = nsScreenManagerGonk::GetInstance();
+ screenManager->AddScreen(GonkDisplay::DISPLAY_VIRTUAL, disp.surface.get());
+ });
+ NS_DispatchToMainThread(runnable, NS_DISPATCH_SYNC);
+ }
+
+ return flags;
+}
+
+void FakeSurfaceComposer::bootFinished()
+{
+}
+
+bool FakeSurfaceComposer::authenticateSurfaceTexture(
+ const sp<IGraphicBufferProducer>& bufferProducer) const {
+ return false;
+}
+
+sp<IDisplayEventConnection> FakeSurfaceComposer::createDisplayEventConnection() {
+ return nullptr;
+}
+
+// ---------------------------------------------------------------------------
+// Capture screen into an IGraphiBufferProducer
+// ---------------------------------------------------------------------------
+
+class Barrier {
+public:
+ inline Barrier() : state(CLOSED) { }
+ inline ~Barrier() { }
+
+ // Release any threads waiting at the Barrier.
+ // Provides release semantics: preceding loads and stores will be visible
+ // to other threads before they wake up.
+ void open() {
+ Mutex::Autolock _l(lock);
+ state = OPENED;
+ cv.broadcast();
+ }
+
+ // Reset the Barrier, so wait() will block until open() has been called.
+ void close() {
+ Mutex::Autolock _l(lock);
+ state = CLOSED;
+ }
+
+ // Wait until the Barrier is OPEN.
+ // Provides acquire semantics: no subsequent loads or stores will occur
+ // until wait() returns.
+ void wait() const {
+ Mutex::Autolock _l(lock);
+ while (state == CLOSED) {
+ cv.wait(lock);
+ }
+ }
+private:
+ enum { OPENED, CLOSED };
+ mutable Mutex lock;
+ mutable Condition cv;
+ volatile int state;
+};
+
+/* The code below is here to handle b/8734824
+ *
+ * We create a IGraphicBufferProducer wrapper that forwards all calls
+ * to the calling binder thread, where they are executed. This allows
+ * the calling thread to be reused (on the other side) and not
+ * depend on having "enough" binder threads to handle the requests.
+ *
+ */
+
+class GraphicProducerWrapper : public BBinder, public MessageHandler {
+ sp<IGraphicBufferProducer> impl;
+ sp<Looper> looper;
+ status_t result;
+ bool exitPending;
+ bool exitRequested;
+ mutable Barrier barrier;
+ volatile int32_t memoryBarrier;
+ uint32_t code;
+ Parcel const* data;
+ Parcel* reply;
+
+ enum {
+ MSG_API_CALL,
+ MSG_EXIT
+ };
+
+ /*
+ * this is called by our "fake" BpGraphicBufferProducer. We package the
+ * data and reply Parcel and forward them to the calling thread.
+ */
+ virtual status_t transact(uint32_t code,
+ const Parcel& data, Parcel* reply, uint32_t flags) {
+ this->code = code;
+ this->data = &data;
+ this->reply = reply;
+ android_atomic_acquire_store(0, &memoryBarrier);
+ if (exitPending) {
+ // if we've exited, we run the message synchronously right here
+ handleMessage(Message(MSG_API_CALL));
+ } else {
+ barrier.close();
+ looper->sendMessage(this, Message(MSG_API_CALL));
+ barrier.wait();
+ }
+ return NO_ERROR;
+ }
+
+ /*
+ * here we run on the binder calling thread. All we've got to do is
+ * call the real BpGraphicBufferProducer.
+ */
+ virtual void handleMessage(const Message& message) {
+ android_atomic_release_load(&memoryBarrier);
+ if (message.what == MSG_API_CALL) {
+ impl->asBinder()->transact(code, data[0], reply);
+ barrier.open();
+ } else if (message.what == MSG_EXIT) {
+ exitRequested = true;
+ }
+ }
+
+public:
+ GraphicProducerWrapper(const sp<IGraphicBufferProducer>& impl) :
+ impl(impl), looper(new Looper(true)), result(NO_ERROR),
+ exitPending(false), exitRequested(false) {
+ }
+
+ status_t waitForResponse() {
+ do {
+ looper->pollOnce(-1);
+ } while (!exitRequested);
+ return result;
+ }
+
+ void exit(status_t result) {
+ this->result = result;
+ exitPending = true;
+ looper->sendMessage(this, Message(MSG_EXIT));
+ }
+};
+
+status_t
+FakeSurfaceComposer::captureScreen(const sp<IBinder>& display
+ , const sp<IGraphicBufferProducer>& producer
+#if ANDROID_VERSION >= 21
+ , Rect sourceCrop
+#endif
+ , uint32_t reqWidth
+ , uint32_t reqHeight
+ , uint32_t minLayerZ
+ , uint32_t maxLayerZ
+#if ANDROID_VERSION >= 21
+ , bool useIdentityTransform
+ , Rotation rotation
+#elif ANDROID_VERSION < 19
+ , bool isCpuConsumer
+#endif
+ )
+{
+ if (display == 0 || producer == 0) {
+ return BAD_VALUE;
+ }
+
+ // Limit only to primary display
+ if (display != mPrimaryDisplay) {
+ return BAD_VALUE;
+ }
+
+ // this creates a "fake" BBinder which will serve as a "fake" remote
+ // binder to receive the marshaled calls and forward them to the
+ // real remote (a BpGraphicBufferProducer)
+ sp<GraphicProducerWrapper> wrapper = new GraphicProducerWrapper(producer);
+ // the asInterface() call below creates our "fake" BpGraphicBufferProducer
+ // which does the marshaling work forwards to our "fake remote" above.
+ sp<IGraphicBufferProducer> fakeProducer = IGraphicBufferProducer::asInterface(wrapper);
+
+ nsCOMPtr<nsIRunnable> runnable =
+ NS_NewRunnableFunction([&]() {
+ captureScreenImp(fakeProducer, reqWidth, reqHeight, wrapper.get());
+ });
+ NS_DispatchToMainThread(runnable);
+
+ status_t result = wrapper->waitForResponse();
+
+ return result;
+}
+
+class RunnableCallTask final : public Runnable
+{
+public:
+ explicit RunnableCallTask(nsIRunnable* aRunnable)
+ : mRunnable(aRunnable) {}
+
+ NS_IMETHOD Run() override
+ {
+ return mRunnable->Run();
+ }
+protected:
+ nsCOMPtr<nsIRunnable> mRunnable;
+};
+
+void
+FakeSurfaceComposer::captureScreenImp(const sp<IGraphicBufferProducer>& producer,
+ uint32_t reqWidth,
+ uint32_t reqHeight,
+ const sp<GraphicProducerWrapper>& wrapper)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(wrapper.get());
+
+ RefPtr<nsScreenGonk> screen = nsScreenManagerGonk::GetPrimaryScreen();
+
+ // get screen geometry
+ nsIntRect screenBounds = screen->GetNaturalBounds().ToUnknownRect();
+ const uint32_t hw_w = screenBounds.width;
+ const uint32_t hw_h = screenBounds.height;
+
+ if (reqWidth > hw_w || reqHeight > hw_h) {
+ ALOGE("size mismatch (%d, %d) > (%d, %d)",
+ reqWidth, reqHeight, hw_w, hw_h);
+ static_cast<GraphicProducerWrapper*>(producer->asBinder().get())->exit(BAD_VALUE);
+ return;
+ }
+
+ reqWidth = (!reqWidth) ? hw_w : reqWidth;
+ reqHeight = (!reqHeight) ? hw_h : reqHeight;
+
+ nsCOMPtr<nsIRunnable> runnable =
+ NS_NewRunnableFunction([screen, reqWidth, reqHeight, producer, wrapper]() {
+ // create a surface (because we're a producer, and we need to
+ // dequeue/queue a buffer)
+ sp<Surface> sur = new Surface(producer);
+ ANativeWindow* window = sur.get();
+ // The closure makes screen const and we can't call forget() on it.
+ RefPtr<nsScreenGonk> screenAlias = screen;
+
+ if (native_window_api_connect(window, NATIVE_WINDOW_API_EGL) != NO_ERROR) {
+ static_cast<GraphicProducerWrapper*>(producer->asBinder().get())->exit(BAD_VALUE);
+ NS_ReleaseOnMainThread(screenAlias.forget());
+ return;
+ }
+ uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
+ GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
+
+ int err = 0;
+ err = native_window_set_buffers_dimensions(window, reqWidth, reqHeight);
+ err |= native_window_set_scaling_mode(window, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+ err |= native_window_set_buffers_format(window, HAL_PIXEL_FORMAT_RGBA_8888);
+ err |= native_window_set_usage(window, usage);
+
+ status_t result = NO_ERROR;
+ if (err == NO_ERROR) {
+ ANativeWindowBuffer* buffer;
+ result = native_window_dequeue_buffer_and_wait(window, &buffer);
+ if (result == NO_ERROR) {
+ nsresult rv = screen->MakeSnapshot(buffer);
+ if (rv != NS_OK) {
+ result = INVALID_OPERATION;
+ }
+ window->queueBuffer(window, buffer, -1);
+ }
+ } else {
+ result = BAD_VALUE;
+ }
+ native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
+ static_cast<GraphicProducerWrapper*>(producer->asBinder().get())->exit(result);
+ NS_ReleaseOnMainThread(screenAlias.forget());
+ });
+
+ layers::CompositorThreadHolder::Loop()->PostTask(
+ MakeAndAddRef<RunnableCallTask>(runnable));
+}
+
+#if ANDROID_VERSION >= 21
+void
+FakeSurfaceComposer::setPowerMode(const sp<IBinder>& display, int mode)
+{
+}
+
+status_t
+FakeSurfaceComposer::getDisplayConfigs(const sp<IBinder>& display, Vector<DisplayInfo>* configs)
+{
+ if (configs == NULL) {
+ return BAD_VALUE;
+ }
+
+ // Limit DisplayConfigs only to primary display
+ if (!display.get() || display != mPrimaryDisplay) {
+ return NAME_NOT_FOUND;
+ }
+
+ configs->clear();
+ DisplayInfo info = DisplayInfo();
+
+ nsCOMPtr<nsIRunnable> runnable =
+ NS_NewRunnableFunction([&]() {
+ MOZ_ASSERT(NS_IsMainThread());
+ getPrimaryDisplayInfo(&info);
+ });
+ NS_DispatchToMainThread(runnable, NS_DISPATCH_SYNC);
+
+ configs->push_back(info);
+ return NO_ERROR;
+}
+
+status_t
+FakeSurfaceComposer::getDisplayStats(const sp<IBinder>& display, DisplayStatInfo* stats)
+{
+ return INVALID_OPERATION;
+}
+
+int
+FakeSurfaceComposer::getActiveConfig(const sp<IBinder>& display)
+{
+ // Only support primary display.
+ if (display.get() && (display == mPrimaryDisplay)) {
+ return 0;
+ }
+ return INVALID_OPERATION;
+}
+
+status_t
+FakeSurfaceComposer::setActiveConfig(const sp<IBinder>& display, int id)
+{
+ return INVALID_OPERATION;
+}
+
+status_t
+FakeSurfaceComposer::clearAnimationFrameStats()
+{
+ return INVALID_OPERATION;
+}
+
+status_t
+FakeSurfaceComposer::getAnimationFrameStats(FrameStats* outStats) const
+{
+ return INVALID_OPERATION;
+}
+#else
+void
+FakeSurfaceComposer::blank(const sp<IBinder>& display)
+{
+}
+
+void
+FakeSurfaceComposer::unblank(const sp<IBinder>& display)
+{
+}
+
+status_t
+FakeSurfaceComposer::getDisplayInfo(const sp<IBinder>& display, DisplayInfo* info)
+{
+ if (info == NULL) {
+ return BAD_VALUE;
+ }
+
+ // Limit DisplayConfigs only to primary display
+ if (!display.get() || display != mPrimaryDisplay) {
+ return NAME_NOT_FOUND;
+ }
+
+ nsCOMPtr<nsIRunnable> runnable =
+ NS_NewRunnableFunction([&]() {
+ MOZ_ASSERT(NS_IsMainThread());
+ getPrimaryDisplayInfo(info);
+ });
+ NS_DispatchToMainThread(runnable, NS_DISPATCH_SYNC);
+
+ return NO_ERROR;
+}
+#endif
+
+#define VSYNC_EVENT_PHASE_OFFSET_NS 0
+#define SF_VSYNC_EVENT_PHASE_OFFSET_NS 0
+
+void
+FakeSurfaceComposer::getPrimaryDisplayInfo(DisplayInfo* info)
+{
+ MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
+
+ // Implementation mimic android SurfaceFlinger::getDisplayConfigs().
+
+ class Density {
+ static int getDensityFromProperty(char const* propName) {
+ char property[PROPERTY_VALUE_MAX];
+ int density = 0;
+ if (property_get(propName, property, NULL) > 0) {
+ density = atoi(property);
+ }
+ return density;
+ }
+ public:
+ static int getEmuDensity() {
+ return getDensityFromProperty("qemu.sf.lcd_density"); }
+ static int getBuildDensity() {
+ return getDensityFromProperty("ro.sf.lcd_density"); }
+ };
+
+ RefPtr<nsScreenGonk> screen = nsScreenManagerGonk::GetPrimaryScreen();
+
+ float xdpi = screen->GetDpi();
+ float ydpi = screen->GetDpi();
+ int fps = 60; // XXX set a value from hwc hal
+ nsIntRect screenBounds = screen->GetNaturalBounds().ToUnknownRect();
+
+ // The density of the device is provided by a build property
+ float density = Density::getBuildDensity() / 160.0f;
+ if (density == 0) {
+ // the build doesn't provide a density -- this is wrong!
+ // use xdpi instead
+ ALOGE("ro.sf.lcd_density must be defined as a build property");
+ density = xdpi / 160.0f;
+ }
+ info->density = density;
+ info->orientation = screen->EffectiveScreenRotation();
+
+ info->w = screenBounds.width;
+ info->h = screenBounds.height;
+ info->xdpi = xdpi;
+ info->ydpi = ydpi;
+ info->fps = fps;
+#if ANDROID_VERSION >= 21
+ info->appVsyncOffset = VSYNC_EVENT_PHASE_OFFSET_NS;
+
+ // This is how far in advance a buffer must be queued for
+ // presentation at a given time. If you want a buffer to appear
+ // on the screen at time N, you must submit the buffer before
+ // (N - presentationDeadline).
+ //
+ // Normally it's one full refresh period (to give SF a chance to
+ // latch the buffer), but this can be reduced by configuring a
+ // DispSync offset. Any additional delays introduced by the hardware
+ // composer or panel must be accounted for here.
+ //
+ // We add an additional 1ms to allow for processing time and
+ // differences between the ideal and actual refresh rate.
+ info->presentationDeadline =
+ (1e9 / fps) - SF_VSYNC_EVENT_PHASE_OFFSET_NS + 1000000;
+#endif
+ // All non-virtual displays are currently considered secure.
+ info->secure = true;
+}
+
+}; // namespace android
diff --git a/widget/gonk/nativewindow/FakeSurfaceComposer.h b/widget/gonk/nativewindow/FakeSurfaceComposer.h
new file mode 100644
index 000000000..97a717444
--- /dev/null
+++ b/widget/gonk/nativewindow/FakeSurfaceComposer.h
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ * Copyright (C) 2013 Mozilla Foundation
+ *
+ * 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 NATIVEWINDOW_FAKE_SURFACE_COMPOSER_H
+#define NATIVEWINDOW_FAKE_SURFACE_COMPOSER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/Looper.h>
+
+#include <binder/BinderService.h>
+
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/ISurfaceComposerClient.h>
+#include <hardware/hwcomposer.h>
+#include <private/gui/LayerState.h>
+#include <utils/KeyedVector.h>
+
+class nsIWidget;
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+class GraphicProducerWrapper;
+class IGraphicBufferAlloc;
+
+enum {
+ eTransactionNeeded = 0x01,
+ eTraversalNeeded = 0x02,
+ eDisplayTransactionNeeded = 0x04,
+ eTransactionMask = 0x07
+};
+
+class FakeSurfaceComposer : public BinderService<FakeSurfaceComposer>,
+ public BnSurfaceComposer
+{
+public:
+ static char const* getServiceName() {
+ return "FakeSurfaceComposer";
+ }
+
+ // Instantiate FakeSurfaceComposer and register to service manager.
+ // If service manager is not present, wait until service manager becomes present.
+ static void instantiate();
+
+#if ANDROID_VERSION >= 19
+ virtual void destroyDisplay(const sp<android::IBinder>& display);
+#endif
+
+#if ANDROID_VERSION >= 21
+ virtual status_t captureScreen(const sp<IBinder>& display,
+ const sp<IGraphicBufferProducer>& producer,
+ Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
+ uint32_t minLayerZ, uint32_t maxLayerZ,
+ bool useIdentityTransform,
+ Rotation rotation = eRotateNone);
+#elif ANDROID_VERSION >= 19
+ virtual status_t captureScreen(const sp<IBinder>& display,
+ const sp<IGraphicBufferProducer>& producer,
+ uint32_t reqWidth, uint32_t reqHeight,
+ uint32_t minLayerZ, uint32_t maxLayerZ);
+#else
+ virtual status_t captureScreen(const sp<IBinder>& display,
+ const sp<IGraphicBufferProducer>& producer,
+ uint32_t reqWidth, uint32_t reqHeight,
+ uint32_t minLayerZ, uint32_t maxLayerZ, bool isCpuConsumer);
+#endif
+
+private:
+ FakeSurfaceComposer();
+ // We're reference counted, never destroy FakeSurfaceComposer directly
+ virtual ~FakeSurfaceComposer();
+
+ /* ------------------------------------------------------------------------
+ * IBinder interface
+ */
+ virtual status_t onTransact(uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags);
+
+ /* ------------------------------------------------------------------------
+ * ISurfaceComposer interface
+ */
+ virtual sp<ISurfaceComposerClient> createConnection();
+ virtual sp<IGraphicBufferAlloc> createGraphicBufferAlloc();
+ virtual sp<IBinder> createDisplay(const String8& displayName, bool secure);
+ virtual sp<IBinder> getBuiltInDisplay(int32_t id);
+ virtual void setTransactionState(const Vector<ComposerState>& state,
+ const Vector<DisplayState>& displays, uint32_t flags);
+ virtual void bootFinished();
+ virtual bool authenticateSurfaceTexture(
+ const sp<IGraphicBufferProducer>& bufferProducer) const;
+ virtual sp<IDisplayEventConnection> createDisplayEventConnection();
+#if ANDROID_VERSION >= 21
+ virtual void setPowerMode(const sp<IBinder>& display, int mode);
+ virtual status_t getDisplayConfigs(const sp<IBinder>& display, Vector<DisplayInfo>* configs);
+ virtual status_t getDisplayStats(const sp<IBinder>& display, DisplayStatInfo* stats);
+ virtual int getActiveConfig(const sp<IBinder>& display);
+ virtual status_t setActiveConfig(const sp<IBinder>& display, int id);
+ virtual status_t clearAnimationFrameStats();
+ virtual status_t getAnimationFrameStats(FrameStats* outStats) const;
+#elif ANDROID_VERSION >= 17
+ // called when screen needs to turn off
+ virtual void blank(const sp<IBinder>& display);
+ // called when screen is turning back on
+ virtual void unblank(const sp<IBinder>& display);
+ virtual status_t getDisplayInfo(const sp<IBinder>& display, DisplayInfo* info);
+#endif
+ void getPrimaryDisplayInfo(DisplayInfo* info);
+
+ /* ------------------------------------------------------------------------
+ * Transactions
+ */
+ uint32_t setDisplayStateLocked(const DisplayState& s);
+
+ void captureScreenImp(const sp<IGraphicBufferProducer>& producer,
+ uint32_t reqWidth,
+ uint32_t reqHeight,
+ const sp<GraphicProducerWrapper>& wrapper);
+
+ sp<IBinder> mPrimaryDisplay;
+
+ struct DisplayDeviceState {
+ enum {
+ NO_LAYER_STACK = 0xFFFFFFFF,
+ };
+ DisplayDeviceState()
+ : type(-1), displayId(-1), width(0), height(0) {
+ }
+ DisplayDeviceState(int type)
+ : type(type), displayId(-1), layerStack(NO_LAYER_STACK), orientation(0), width(0), height(0) {
+ viewport.makeInvalid();
+ frame.makeInvalid();
+ }
+ bool isValid() const { return type >= 0; }
+ int type;
+ int displayId;
+ sp<IGraphicBufferProducer> surface;
+ uint32_t layerStack;
+ Rect viewport;
+ Rect frame;
+ uint8_t orientation;
+ uint32_t width, height;
+ String8 displayName;
+ bool isSecure;
+ };
+
+ // access must be protected by mStateLock
+ mutable Mutex mStateLock;
+ DefaultKeyedVector<wp<IBinder>, DisplayDeviceState> mDisplays;
+
+ friend class DestroyDisplayRunnable;
+};
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif // NATIVEWINDOW_FAKE_SURFACE_COMPOSER_H
diff --git a/widget/gonk/nativewindow/GonkBufferQueue.h b/widget/gonk/nativewindow/GonkBufferQueue.h
new file mode 100644
index 000000000..defdb0ae2
--- /dev/null
+++ b/widget/gonk/nativewindow/GonkBufferQueue.h
@@ -0,0 +1,22 @@
+/* Copyright 2013 Mozilla Foundation and Mozilla contributors
+ *
+ * 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.
+ */
+
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21
+# include "GonkBufferQueueLL.h"
+#elif defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 19
+# include "GonkBufferQueueKK.h"
+#elif defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
+# include "GonkBufferQueueJB.h"
+#endif
diff --git a/widget/gonk/nativewindow/GonkBufferQueueJB.cpp b/widget/gonk/nativewindow/GonkBufferQueueJB.cpp
new file mode 100644
index 000000000..81502f81e
--- /dev/null
+++ b/widget/gonk/nativewindow/GonkBufferQueueJB.cpp
@@ -0,0 +1,1036 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2013 Mozilla Foundation
+ *
+ * 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 "GonkBufferQueue"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#define LOG_NDEBUG 0
+
+#define GL_GLEXT_PROTOTYPES
+#define EGL_EGLEXT_PROTOTYPES
+
+#include <utils/Log.h>
+
+#include "mozilla/layers/GrallocTextureClient.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+
+#include "GonkBufferQueueJB.h"
+
+// Macros for including the GonkBufferQueue name in log messages
+#define ST_LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
+#define ST_LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
+#define ST_LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
+#define ST_LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
+#define ST_LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
+
+#define ATRACE_BUFFER_INDEX(index)
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+
+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);
+}
+
+static const char* scalingModeName(int scalingMode) {
+ switch (scalingMode) {
+ case NATIVE_WINDOW_SCALING_MODE_FREEZE: return "FREEZE";
+ case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: return "SCALE_TO_WINDOW";
+ case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP: return "SCALE_CROP";
+ default: return "Unknown";
+ }
+}
+
+GonkBufferQueue::GonkBufferQueue(bool allowSynchronousMode,
+ const sp<IGraphicBufferAlloc>& allocator) :
+ mDefaultWidth(1),
+ mDefaultHeight(1),
+ mMaxAcquiredBufferCount(1),
+ mDefaultMaxBufferCount(2),
+ mOverrideMaxBufferCount(0),
+ mSynchronousMode(true),
+ mAllowSynchronousMode(allowSynchronousMode),
+ mConnectedApi(NO_CONNECTED_API),
+ mAbandoned(false),
+ mFrameCounter(0),
+ mBufferHasBeenQueued(false),
+ mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888),
+ mConsumerUsageBits(0),
+ mTransformHint(0)
+{
+ // Choose a name using the PID and a process-unique ID.
+ mConsumerName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
+
+ ST_LOGV("GonkBufferQueue");
+}
+
+GonkBufferQueue::~GonkBufferQueue() {
+ ST_LOGV("~GonkBufferQueue");
+}
+
+status_t GonkBufferQueue::setDefaultMaxBufferCountLocked(int count) {
+ if (count < 2 || count > NUM_BUFFER_SLOTS)
+ return BAD_VALUE;
+
+ mDefaultMaxBufferCount = count;
+ mDequeueCondition.broadcast();
+
+ return NO_ERROR;
+}
+
+bool GonkBufferQueue::isSynchronousMode() const {
+ Mutex::Autolock lock(mMutex);
+ return mSynchronousMode;
+}
+
+void GonkBufferQueue::setConsumerName(const String8& name) {
+ Mutex::Autolock lock(mMutex);
+ mConsumerName = name;
+}
+
+status_t GonkBufferQueue::setDefaultBufferFormat(uint32_t defaultFormat) {
+ Mutex::Autolock lock(mMutex);
+ mDefaultBufferFormat = defaultFormat;
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueue::setConsumerUsageBits(uint32_t usage) {
+ Mutex::Autolock lock(mMutex);
+ mConsumerUsageBits = usage;
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueue::setTransformHint(uint32_t hint) {
+ ST_LOGV("setTransformHint: %02x", hint);
+ Mutex::Autolock lock(mMutex);
+ mTransformHint = hint;
+ return NO_ERROR;
+}
+
+already_AddRefed<TextureClient>
+GonkBufferQueue::getTextureClientFromBuffer(ANativeWindowBuffer* buffer)
+{
+ Mutex::Autolock _l(mMutex);
+ if (buffer == NULL) {
+ ST_LOGE("getSlotFromBufferLocked: encountered NULL buffer");
+ return nullptr;
+ }
+
+ for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
+ if (mSlots[i].mGraphicBuffer != NULL && mSlots[i].mGraphicBuffer->handle == buffer->handle) {
+ RefPtr<TextureClient> client(mSlots[i].mTextureClient);
+ return client.forget();
+ }
+ }
+ ST_LOGE("getSlotFromBufferLocked: unknown buffer: %p", buffer->handle);
+ return nullptr;
+}
+
+int GonkBufferQueue::getSlotFromTextureClientLocked(
+ TextureClient* client) const
+{
+ if (client == NULL) {
+ ST_LOGE("getSlotFromBufferLocked: encountered NULL buffer");
+ return BAD_VALUE;
+ }
+
+ for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
+ if (mSlots[i].mTextureClient == client) {
+ return i;
+ }
+ }
+ ST_LOGE("getSlotFromBufferLocked: unknown TextureClient: %p", client);
+ return BAD_VALUE;
+}
+
+
+status_t GonkBufferQueue::setBufferCount(int bufferCount) {
+ ST_LOGV("setBufferCount: count=%d", bufferCount);
+
+ sp<ConsumerListener> listener;
+ {
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ ST_LOGE("setBufferCount: GonkBufferQueue has been abandoned!");
+ return NO_INIT;
+ }
+ if (bufferCount > NUM_BUFFER_SLOTS) {
+ ST_LOGE("setBufferCount: bufferCount too large (max %d)",
+ NUM_BUFFER_SLOTS);
+ return BAD_VALUE;
+ }
+
+ // Error out if the user has dequeued buffers
+ int maxBufferCount = getMaxBufferCountLocked();
+ for (int i=0 ; i<maxBufferCount; i++) {
+ if (mSlots[i].mBufferState == BufferSlot::DEQUEUED) {
+ ST_LOGE("setBufferCount: client owns some buffers");
+ return -EINVAL;
+ }
+ }
+
+ const int minBufferSlots = getMinMaxBufferCountLocked();
+ if (bufferCount == 0) {
+ mOverrideMaxBufferCount = 0;
+ mDequeueCondition.broadcast();
+ return NO_ERROR;
+ }
+
+ 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.
+ //
+ // XXX: Should this use drainQueueAndFreeBuffersLocked instead?
+ freeAllBuffersLocked();
+ mOverrideMaxBufferCount = bufferCount;
+ mBufferHasBeenQueued = false;
+ mDequeueCondition.broadcast();
+ listener = mConsumerListener;
+ } // scope for lock
+
+ if (listener != NULL) {
+ listener->onBuffersReleased();
+ }
+
+ return NO_ERROR;
+}
+
+int GonkBufferQueue::query(int what, int* outValue)
+{
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ ST_LOGE("query: GonkBufferQueue has been abandoned!");
+ return NO_INIT;
+ }
+
+ int value;
+ switch (what) {
+ case NATIVE_WINDOW_WIDTH:
+ value = mDefaultWidth;
+ break;
+ case NATIVE_WINDOW_HEIGHT:
+ value = mDefaultHeight;
+ break;
+ case NATIVE_WINDOW_FORMAT:
+ value = mDefaultBufferFormat;
+ break;
+ case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
+ value = getMinUndequeuedBufferCountLocked();
+ break;
+ case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND:
+ value = (mQueue.size() >= 2);
+ break;
+ default:
+ return BAD_VALUE;
+ }
+ outValue[0] = value;
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueue::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
+ ST_LOGV("requestBuffer: slot=%d", slot);
+ Mutex::Autolock lock(mMutex);
+ if (mAbandoned) {
+ ST_LOGE("requestBuffer: GonkBufferQueue has been abandoned!");
+ return NO_INIT;
+ }
+ int maxBufferCount = getMaxBufferCountLocked();
+ if (slot < 0 || maxBufferCount <= slot) {
+ ST_LOGE("requestBuffer: slot index out of range [0, %d]: %d",
+ maxBufferCount, slot);
+ return BAD_VALUE;
+ } else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) {
+ // XXX: I vaguely recall there was some reason this can be valid, but
+ // for the life of me I can't recall under what circumstances that's
+ // the case.
+ ST_LOGE("requestBuffer: slot %d is not owned by the client (state=%d)",
+ slot, mSlots[slot].mBufferState);
+ return BAD_VALUE;
+ }
+ mSlots[slot].mRequestBufferCalled = true;
+ *buf = mSlots[slot].mGraphicBuffer;
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueue::dequeueBuffer(int *outBuf, sp<Fence>* outFence,
+ 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);
+ int buf = INVALID_BUFFER_SLOT;
+
+ { // Scope for the lock
+ Mutex::Autolock lock(mMutex);
+
+ if (format == 0) {
+ format = mDefaultBufferFormat;
+ }
+ // turn on usage bits the consumer requested
+ usage |= mConsumerUsageBits;
+
+ int found = -1;
+ int dequeuedCount = 0;
+ bool tryAgain = true;
+ while (tryAgain) {
+ if (mAbandoned) {
+ ST_LOGE("dequeueBuffer: GonkBufferQueue has been abandoned!");
+ return NO_INIT;
+ }
+
+ const int maxBufferCount = getMaxBufferCountLocked();
+
+ // Free up any buffers that are in slots beyond the max buffer
+ // count.
+ //for (int i = maxBufferCount; i < NUM_BUFFER_SLOTS; i++) {
+ // assert(mSlots[i].mBufferState == BufferSlot::FREE);
+ // if (mSlots[i].mGraphicBuffer != NULL) {
+ // freeBufferLocked(i);
+ // returnFlags |= IGraphicBufferProducer::RELEASE_ALL_BUFFERS;
+ // }
+ //}
+
+ // look for a free buffer to give to the client
+ found = INVALID_BUFFER_SLOT;
+ dequeuedCount = 0;
+ for (int i = 0; i < maxBufferCount; i++) {
+ const int state = mSlots[i].mBufferState;
+ if (state == BufferSlot::DEQUEUED) {
+ dequeuedCount++;
+ }
+
+ 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.
+ */
+ if ((found < 0) ||
+ mSlots[i].mFrameNumber < mSlots[found].mFrameNumber) {
+ found = i;
+ }
+ }
+ }
+
+ // clients are not allowed to dequeue more than one buffer
+ // if they didn't set a buffer count.
+ if (!mOverrideMaxBufferCount && 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.
+ if (mBufferHasBeenQueued) {
+ // make sure the client is not trying to dequeue more buffers
+ // than allowed.
+ const int newUndequeuedCount = maxBufferCount - (dequeuedCount+1);
+ const int minUndequeuedCount = getMinUndequeuedBufferCountLocked();
+ if (newUndequeuedCount < minUndequeuedCount) {
+ ST_LOGE("dequeueBuffer: min undequeued buffer count (%d) "
+ "exceeded (dequeued=%d undequeudCount=%d)",
+ minUndequeuedCount, dequeuedCount,
+ newUndequeuedCount);
+ return -EBUSY;
+ }
+ }
+
+ // If no buffer is found, wait for a buffer to be released or for
+ // the max buffer count to change.
+ tryAgain = found == INVALID_BUFFER_SLOT;
+ if (tryAgain) {
+ mDequeueCondition.wait(mMutex);
+ }
+ }
+
+
+ if (found == INVALID_BUFFER_SLOT) {
+ // This should not happen.
+ ST_LOGE("dequeueBuffer: no available buffer slots");
+ return -EBUSY;
+ }
+
+ buf = found;
+ *outBuf = found;
+
+ const bool useDefaultSize = !w && !h;
+ if (useDefaultSize) {
+ // use the default size
+ w = mDefaultWidth;
+ h = mDefaultHeight;
+ }
+
+ 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))
+ {
+ mSlots[buf].mAcquireCalled = false;
+ mSlots[buf].mGraphicBuffer = NULL;
+ mSlots[buf].mRequestBufferCalled = false;
+ mSlots[buf].mFence = Fence::NO_FENCE;
+ if (mSlots[buf].mTextureClient) {
+ mSlots[buf].mTextureClient->ClearRecycleCallback();
+ // release TextureClient in ImageBridge thread
+ RefPtr<TextureClientReleaseTask> task =
+ MakeAndAddRef<TextureClientReleaseTask>(mSlots[buf].mTextureClient);
+ mSlots[buf].mTextureClient = NULL;
+ ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(task.forget());
+ }
+ returnFlags |= IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION;
+ }
+
+ *outFence = mSlots[buf].mFence;
+ mSlots[buf].mFence = Fence::NO_FENCE;
+ } // end lock scope
+
+ sp<GraphicBuffer> graphicBuffer;
+ if (returnFlags & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) {
+
+ usage |= GraphicBuffer::USAGE_HW_TEXTURE;
+ RefPtr<LayersIPCChannel> allocator = ImageBridgeChild::GetSingleton();
+ GrallocTextureData* texData = GrallocTextureData::Create(IntSize(w,h), format,
+ gfx::BackendType::NONE, usage,
+ allocator);
+ if (!texData) {
+ ST_LOGE("dequeueBuffer: failed to alloc gralloc buffer");
+ return -ENOMEM;
+ }
+ RefPtr<TextureClient> textureClient = new TextureClient(texData, TextureFlags::RECYCLE | TextureFlags::DEALLOCATE_CLIENT, allocator);
+ sp<GraphicBuffer> graphicBuffer = texData->GetGraphicBuffer();
+
+ { // Scope for the lock
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ ST_LOGE("dequeueBuffer: SurfaceTexture has been abandoned!");
+ return NO_INIT;
+ }
+
+ mSlots[buf].mGraphicBuffer = graphicBuffer;
+ mSlots[buf].mTextureClient = textureClient;
+ ST_LOGD("dequeueBuffer: returning slot=%d buf=%p ", buf,
+ mSlots[buf].mGraphicBuffer->handle);
+ //mSlots[*outBuf].mGraphicBuffer = graphicBuffer;
+ }
+ }
+
+ ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", *outBuf,
+ mSlots[*outBuf].mGraphicBuffer->handle, returnFlags);
+
+ return returnFlags;
+}
+
+status_t GonkBufferQueue::setSynchronousMode(bool enabled) {
+ ST_LOGV("setSynchronousMode: enabled=%d", enabled);
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ ST_LOGE("setSynchronousMode: BufferQueue has been abandoned!");
+ return NO_INIT;
+ }
+
+ if (mSynchronousMode != enabled) {
+ mSynchronousMode = enabled;
+ mDequeueCondition.broadcast();
+ }
+ return OK;
+}
+
+status_t GonkBufferQueue::queueBuffer(int buf,
+ const QueueBufferInput& input, QueueBufferOutput* output) {
+
+ Rect crop;
+ uint32_t transform;
+ int scalingMode;
+ int64_t timestamp;
+ sp<Fence> fence;
+
+ input.deflate(&timestamp, &crop, &scalingMode, &transform, &fence);
+
+#if ANDROID_VERSION >= 18
+ if (fence == NULL) {
+ ST_LOGE("queueBuffer: fence is NULL");
+ return BAD_VALUE;
+ }
+#endif
+
+ ST_LOGV("queueBuffer: slot=%d time=%#llx crop=[%d,%d,%d,%d] tr=%#x "
+ "scale=%s",
+ buf, timestamp, crop.left, crop.top, crop.right, crop.bottom,
+ transform, scalingModeName(scalingMode));
+
+ sp<ConsumerListener> listener;
+
+ { // scope for the lock
+ Mutex::Autolock lock(mMutex);
+ if (mAbandoned) {
+ ST_LOGE("queueBuffer: GonkBufferQueue has been abandoned!");
+ return NO_INIT;
+ }
+ int maxBufferCount = getMaxBufferCountLocked();
+ if (buf < 0 || buf >= maxBufferCount) {
+ ST_LOGE("queueBuffer: slot index out of range [0, %d]: %d",
+ maxBufferCount, 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 (!mSlots[buf].mRequestBufferCalled) {
+ ST_LOGE("queueBuffer: slot %d was enqueued without requesting a "
+ "buffer", buf);
+ return -EINVAL;
+ }
+
+ const sp<GraphicBuffer>& graphicBuffer(mSlots[buf].mGraphicBuffer);
+ Rect bufferRect(graphicBuffer->getWidth(), graphicBuffer->getHeight());
+ Rect croppedCrop;
+ crop.intersect(bufferRect, &croppedCrop);
+ if (croppedCrop != crop) {
+ ST_LOGE("queueBuffer: crop rect is not contained within the "
+ "buffer in slot %d", buf);
+ return -EINVAL;
+ }
+
+ if (mSynchronousMode) {
+ // In synchronous mode we queue all buffers in a FIFO.
+ mQueue.push_back(buf);
+ } else {
+ // In asynchronous mode we only keep the most recent buffer.
+ if (mQueue.empty()) {
+ mQueue.push_back(buf);
+ } 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;
+ }
+ }
+ // always signals that an additional frame should be consumed
+ // to handle max acquired buffer count reached case.
+ listener = mConsumerListener;
+
+ mSlots[buf].mTimestamp = timestamp;
+ mSlots[buf].mCrop = crop;
+ mSlots[buf].mTransform = transform;
+ mSlots[buf].mFence = fence;
+
+ switch (scalingMode) {
+ case NATIVE_WINDOW_SCALING_MODE_FREEZE:
+ case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
+ case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP:
+ break;
+ default:
+ ST_LOGE("unknown scaling mode: %d (ignoring)", scalingMode);
+ scalingMode = mSlots[buf].mScalingMode;
+ break;
+ }
+
+ mSlots[buf].mBufferState = BufferSlot::QUEUED;
+ mSlots[buf].mScalingMode = scalingMode;
+ mFrameCounter++;
+ mSlots[buf].mFrameNumber = mFrameCounter;
+
+ mBufferHasBeenQueued = true;
+ mDequeueCondition.broadcast();
+
+ output->inflate(mDefaultWidth, mDefaultHeight, mTransformHint,
+ mQueue.size());
+ } // scope for the lock
+
+ // call back without lock held
+ if (listener != 0) {
+ listener->onFrameAvailable();
+ }
+ return NO_ERROR;
+}
+
+#if ANDROID_VERSION == 17
+void GonkBufferQueue::cancelBuffer(int buf, sp<Fence> fence) {
+#else
+void GonkBufferQueue::cancelBuffer(int buf, const sp<Fence>& fence) {
+#endif
+
+ ST_LOGV("cancelBuffer: slot=%d", buf);
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ ST_LOGW("cancelBuffer: GonkBufferQueue has been abandoned!");
+ return;
+ }
+
+ int maxBufferCount = getMaxBufferCountLocked();
+ if (buf < 0 || buf >= maxBufferCount) {
+ ST_LOGE("cancelBuffer: slot index out of range [0, %d]: %d",
+ maxBufferCount, 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;
+#if ANDROID_VERSION >= 18
+ } else if (fence == NULL) {
+ ST_LOGE("cancelBuffer: fence is NULL");
+ return;
+#endif
+ }
+ mSlots[buf].mBufferState = BufferSlot::FREE;
+ mSlots[buf].mFrameNumber = 0;
+ mSlots[buf].mFence = fence;
+ mDequeueCondition.broadcast();
+}
+
+status_t GonkBufferQueue::connect(int api, QueueBufferOutput* output) {
+ ST_LOGV("connect: api=%d", api);
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ ST_LOGE("connect: GonkBufferQueue has been abandoned!");
+ return NO_INIT;
+ }
+
+ if (mConsumerListener == NULL) {
+ ST_LOGE("connect: GonkBufferQueue has no consumer!");
+ 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;
+ output->inflate(mDefaultWidth, mDefaultHeight, mTransformHint,
+ mQueue.size());
+ }
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ mBufferHasBeenQueued = false;
+
+ return err;
+}
+
+status_t GonkBufferQueue::disconnect(int api) {
+ ST_LOGV("disconnect: api=%d", api);
+
+ int err = NO_ERROR;
+ sp<ConsumerListener> listener;
+
+ { // Scope for the lock
+ 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;
+ }
+
+ 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) {
+ freeAllBuffersLocked();
+ mConnectedApi = NO_CONNECTED_API;
+ mDequeueCondition.broadcast();
+ listener = mConsumerListener;
+ } 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;
+ }
+ }
+
+ if (listener != NULL) {
+ listener->onBuffersReleased();
+ }
+
+ return err;
+}
+
+void GonkBufferQueue::dumpToString(String8& result) const
+{
+ char buffer[1024];
+ GonkBufferQueue::dumpToString(result, "", buffer, 1024);
+}
+
+void GonkBufferQueue::dumpToString(String8& result, const char* prefix,
+ char* buffer, size_t SIZE) const
+{
+ Mutex::Autolock _l(mMutex);
+
+ String8 fifo;
+ int fifoSize = 0;
+ Fifo::const_iterator i(mQueue.begin());
+ while (i != mQueue.end()) {
+ snprintf(buffer, SIZE, "%02d ", *i++);
+ fifoSize++;
+ fifo.append(buffer);
+ }
+
+ int maxBufferCount = getMaxBufferCountLocked();
+
+ snprintf(buffer, SIZE,
+ "%s-BufferQueue maxBufferCount=%d, mSynchronousMode=%d, default-size=[%dx%d], "
+ "default-format=%d, transform-hint=%02x, FIFO(%d)={%s}\n",
+ prefix, maxBufferCount, mSynchronousMode, mDefaultWidth,
+ mDefaultHeight, mDefaultBufferFormat, mTransformHint,
+ fifoSize, fifo.string());
+ result.append(buffer);
+
+
+ struct {
+ const char * operator()(int state) const {
+ switch (state) {
+ case BufferSlot::DEQUEUED: return "DEQUEUED";
+ case BufferSlot::QUEUED: return "QUEUED";
+ case BufferSlot::FREE: return "FREE";
+ case BufferSlot::ACQUIRED: return "ACQUIRED";
+ default: return "Unknown";
+ }
+ }
+ } stateName;
+
+ for (int i=0 ; i<maxBufferCount ; i++) {
+ const BufferSlot& slot(mSlots[i]);
+ snprintf(buffer, SIZE,
+ "%s%s[%02d] "
+ "state=%-8s, crop=[%d,%d,%d,%d], "
+ "xform=0x%02x, time=%#llx, scale=%s",
+ prefix, (slot.mBufferState == BufferSlot::ACQUIRED)?">":" ", i,
+ stateName(slot.mBufferState),
+ slot.mCrop.left, slot.mCrop.top, slot.mCrop.right,
+ slot.mCrop.bottom, slot.mTransform, slot.mTimestamp,
+ scalingModeName(slot.mScalingMode)
+ );
+ result.append(buffer);
+
+ const sp<GraphicBuffer>& buf(slot.mGraphicBuffer);
+ if (buf != NULL) {
+ snprintf(buffer, SIZE,
+ ", %p [%4ux%4u:%4u,%3X]",
+ buf->handle, buf->width, buf->height, buf->stride,
+ buf->format);
+ result.append(buffer);
+ }
+ result.append("\n");
+ }
+}
+
+void GonkBufferQueue::freeAllBuffersLocked()
+{
+ ALOGW_IF(!mQueue.isEmpty(),
+ "freeAllBuffersLocked called but mQueue is not empty");
+ mQueue.clear();
+ mBufferHasBeenQueued = false;
+ for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
+ mSlots[i].mGraphicBuffer = 0;
+ if (mSlots[i].mTextureClient) {
+ mSlots[i].mTextureClient->ClearRecycleCallback();
+ // release TextureClient in ImageBridge thread
+ RefPtr<TextureClientReleaseTask> task =
+ MakeAndAddRef<TextureClientReleaseTask>(mSlots[i].mTextureClient);
+ mSlots[i].mTextureClient = NULL;
+ ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(task.forget());
+ }
+ if (mSlots[i].mBufferState == BufferSlot::ACQUIRED) {
+ mSlots[i].mNeedsCleanupOnRelease = true;
+ }
+ mSlots[i].mBufferState = BufferSlot::FREE;
+ mSlots[i].mFrameNumber = 0;
+ mSlots[i].mAcquireCalled = false;
+ // destroy fence as GonkBufferQueue now takes ownership
+ mSlots[i].mFence = Fence::NO_FENCE;
+ }
+}
+
+status_t GonkBufferQueue::acquireBuffer(BufferItem *buffer) {
+ Mutex::Autolock _l(mMutex);
+
+ // Check that the consumer doesn't currently have the maximum number of
+ // buffers acquired. We allow the max buffer count to be exceeded by one
+ // buffer, so that the consumer can successfully set up the newly acquired
+ // buffer before releasing the old one.
+ int numAcquiredBuffers = 0;
+ for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
+ if (mSlots[i].mBufferState == BufferSlot::ACQUIRED) {
+ numAcquiredBuffers++;
+ }
+ }
+ if (numAcquiredBuffers >= mMaxAcquiredBufferCount+1) {
+ ST_LOGE("acquireBuffer: max acquired buffer count reached: %d (max=%d)",
+ numAcquiredBuffers, mMaxAcquiredBufferCount);
+ return INVALID_OPERATION;
+ }
+
+ // check if queue is empty
+ // In asynchronous mode the list is guaranteed to be one buffer
+ // deep, while in synchronous mode we use the oldest buffer.
+ if (!mQueue.empty()) {
+ Fifo::iterator front(mQueue.begin());
+ int buf = *front;
+
+ // In android, when the buffer is aquired by BufferConsumer,
+ // BufferQueue releases a reference to the buffer and
+ // it's ownership moves to the BufferConsumer.
+ // In b2g, GonkBufferQueue continues to have a buffer ownership.
+ // It is necessary to free buffer via ImageBridgeChild.
+
+ //if (mSlots[buf].mAcquireCalled) {
+ // buffer->mGraphicBuffer = NULL;
+ //} else {
+ // buffer->mGraphicBuffer = mSlots[buf].mGraphicBuffer;
+ //}
+ buffer->mGraphicBuffer = mSlots[buf].mGraphicBuffer;
+ buffer->mCrop = mSlots[buf].mCrop;
+ buffer->mTransform = mSlots[buf].mTransform;
+ buffer->mScalingMode = mSlots[buf].mScalingMode;
+ buffer->mFrameNumber = mSlots[buf].mFrameNumber;
+ buffer->mTimestamp = mSlots[buf].mTimestamp;
+ buffer->mBuf = buf;
+ buffer->mFence = mSlots[buf].mFence;
+
+ mSlots[buf].mAcquireCalled = true;
+ mSlots[buf].mNeedsCleanupOnRelease = false;
+ mSlots[buf].mBufferState = BufferSlot::ACQUIRED;
+ mSlots[buf].mFence = Fence::NO_FENCE;
+
+ mQueue.erase(front);
+ mDequeueCondition.broadcast();
+ } else {
+ return NO_BUFFER_AVAILABLE;
+ }
+
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueue::releaseBuffer(int buf, const sp<Fence>& fence) {
+ Mutex::Autolock _l(mMutex);
+
+#if ANDROID_VERSION == 17
+ if (buf == INVALID_BUFFER_SLOT) {
+#else
+ if (buf == INVALID_BUFFER_SLOT || fence == NULL) {
+#endif
+ return BAD_VALUE;
+ }
+
+ mSlots[buf].mFence = fence;
+
+ // The buffer can now only be released if its in the acquired state
+ if (mSlots[buf].mBufferState == BufferSlot::ACQUIRED) {
+ mSlots[buf].mBufferState = BufferSlot::FREE;
+ } else if (mSlots[buf].mNeedsCleanupOnRelease) {
+ ST_LOGV("releasing a stale buf %d its state was %d", buf, mSlots[buf].mBufferState);
+ mSlots[buf].mNeedsCleanupOnRelease = false;
+ return STALE_BUFFER_SLOT;
+ } else {
+ ST_LOGE("attempted to release buf %d but its state was %d", buf, mSlots[buf].mBufferState);
+ return -EINVAL;
+ }
+
+ mDequeueCondition.broadcast();
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueue::consumerConnect(const sp<ConsumerListener>& consumerListener) {
+ ST_LOGV("consumerConnect");
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ ST_LOGE("consumerConnect: GonkBufferQueue has been abandoned!");
+ return NO_INIT;
+ }
+ if (consumerListener == NULL) {
+ ST_LOGE("consumerConnect: consumerListener may not be NULL");
+ return BAD_VALUE;
+ }
+
+ mConsumerListener = consumerListener;
+
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueue::consumerDisconnect() {
+ ST_LOGV("consumerDisconnect");
+ Mutex::Autolock lock(mMutex);
+
+ if (mConsumerListener == NULL) {
+ ST_LOGE("consumerDisconnect: No consumer is connected!");
+ return -EINVAL;
+ }
+
+ mAbandoned = true;
+ mConsumerListener = NULL;
+ mQueue.clear();
+ freeAllBuffersLocked();
+ mDequeueCondition.broadcast();
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueue::getReleasedBuffers(uint32_t* slotMask) {
+ ST_LOGV("getReleasedBuffers");
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ ST_LOGE("getReleasedBuffers: GonkBufferQueue has been abandoned!");
+ return NO_INIT;
+ }
+
+ uint32_t mask = 0;
+ for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
+ if (!mSlots[i].mAcquireCalled) {
+ mask |= 1 << i;
+ }
+ }
+ *slotMask = mask;
+
+ ST_LOGV("getReleasedBuffers: returning mask %#x", mask);
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueue::setDefaultBufferSize(uint32_t w, uint32_t h)
+{
+ ST_LOGV("setDefaultBufferSize: w=%d, h=%d", w, h);
+ if (!w || !h) {
+ ST_LOGE("setDefaultBufferSize: dimensions cannot be 0 (w=%d, h=%d)",
+ w, h);
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mMutex);
+ mDefaultWidth = w;
+ mDefaultHeight = h;
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueue::setDefaultMaxBufferCount(int bufferCount) {
+ Mutex::Autolock lock(mMutex);
+ return setDefaultMaxBufferCountLocked(bufferCount);
+}
+
+status_t GonkBufferQueue::setMaxAcquiredBufferCount(int maxAcquiredBuffers) {
+ Mutex::Autolock lock(mMutex);
+ if (maxAcquiredBuffers < 1 || maxAcquiredBuffers > MAX_MAX_ACQUIRED_BUFFERS) {
+ ST_LOGE("setMaxAcquiredBufferCount: invalid count specified: %d",
+ maxAcquiredBuffers);
+ return BAD_VALUE;
+ }
+ if (mConnectedApi != NO_CONNECTED_API) {
+ return INVALID_OPERATION;
+ }
+ mMaxAcquiredBufferCount = maxAcquiredBuffers;
+ return NO_ERROR;
+}
+
+int GonkBufferQueue::getMinMaxBufferCountLocked() const {
+ return getMinUndequeuedBufferCountLocked() + 1;
+}
+
+int GonkBufferQueue::getMinUndequeuedBufferCountLocked() const {
+ return mSynchronousMode ? mMaxAcquiredBufferCount :
+ mMaxAcquiredBufferCount + 1;
+}
+
+int GonkBufferQueue::getMaxBufferCountLocked() const {
+ int minMaxBufferCount = getMinMaxBufferCountLocked();
+
+ int maxBufferCount = mDefaultMaxBufferCount;
+ if (maxBufferCount < minMaxBufferCount) {
+ maxBufferCount = minMaxBufferCount;
+ }
+ if (mOverrideMaxBufferCount != 0) {
+ assert(mOverrideMaxBufferCount >= minMaxBufferCount);
+ maxBufferCount = mOverrideMaxBufferCount;
+ }
+
+ // Any buffers that are dequeued by the producer or sitting in the queue
+ // waiting to be consumed need to have their slots preserved. Such
+ // buffers will temporarily keep the max buffer count up until the slots
+ // no longer need to be preserved.
+ for (int i = maxBufferCount; i < NUM_BUFFER_SLOTS; i++) {
+ BufferSlot::BufferState state = mSlots[i].mBufferState;
+ if (state == BufferSlot::QUEUED || state == BufferSlot::DEQUEUED) {
+ maxBufferCount = i + 1;
+ }
+ }
+
+ return maxBufferCount;
+}
+
+GonkBufferQueue::ProxyConsumerListener::ProxyConsumerListener(
+ const wp<GonkBufferQueue::ConsumerListener>& consumerListener):
+ mConsumerListener(consumerListener) {}
+
+GonkBufferQueue::ProxyConsumerListener::~ProxyConsumerListener() {}
+
+void GonkBufferQueue::ProxyConsumerListener::onFrameAvailable() {
+ sp<GonkBufferQueue::ConsumerListener> listener(mConsumerListener.promote());
+ if (listener != NULL) {
+ listener->onFrameAvailable();
+ }
+}
+
+void GonkBufferQueue::ProxyConsumerListener::onBuffersReleased() {
+ sp<GonkBufferQueue::ConsumerListener> listener(mConsumerListener.promote());
+ if (listener != NULL) {
+ listener->onBuffersReleased();
+ }
+}
+
+}; // namespace android
diff --git a/widget/gonk/nativewindow/GonkBufferQueueJB.h b/widget/gonk/nativewindow/GonkBufferQueueJB.h
new file mode 100644
index 000000000..df0c4599f
--- /dev/null
+++ b/widget/gonk/nativewindow/GonkBufferQueueJB.h
@@ -0,0 +1,653 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2013 Mozilla Foundation
+ *
+ * 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 NATIVEWINDOW_GONKBUFFERQUEUE_JB_H
+#define NATIVEWINDOW_GONKBUFFERQUEUE_JB_H
+
+#include <gui/IGraphicBufferAlloc.h>
+#if ANDROID_VERSION == 17
+#include <gui/ISurfaceTexture.h>
+#else
+#include <gui/IGraphicBufferProducer.h>
+#endif
+
+#include <ui/Fence.h>
+#include <ui/GraphicBuffer.h>
+
+#include <utils/String8.h>
+#include <utils/Vector.h>
+#include <utils/threads.h>
+
+#include "mozilla/layers/LayersSurfaces.h"
+#include "mozilla/layers/TextureClient.h"
+
+#if ANDROID_VERSION == 17
+#define IGraphicBufferProducer ISurfaceTexture
+#endif
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+#if ANDROID_VERSION == 17
+class GonkBufferQueue : public BnSurfaceTexture {
+#else
+class GonkBufferQueue : public BnGraphicBufferProducer {
+#endif
+ typedef mozilla::layers::TextureClient TextureClient;
+
+public:
+ enum { MIN_UNDEQUEUED_BUFFERS = 2 };
+ enum { NUM_BUFFER_SLOTS = 32 };
+ enum { NO_CONNECTED_API = 0 };
+ enum { INVALID_BUFFER_SLOT = -1 };
+ enum { STALE_BUFFER_SLOT = 1, NO_BUFFER_AVAILABLE };
+
+ // When in async mode we reserve two slots in order to guarantee that the
+ // producer and consumer can run asynchronously.
+ enum { MAX_MAX_ACQUIRED_BUFFERS = NUM_BUFFER_SLOTS - 2 };
+
+ // ConsumerListener is the interface through which the GonkBufferQueue notifies
+ // the consumer of events that the consumer may wish to react to. Because
+ // the consumer will generally have a mutex that is locked during calls from
+ // the consumer to the GonkBufferQueue, these calls from the GonkBufferQueue to the
+ // consumer *MUST* be called only when the GonkBufferQueue mutex is NOT locked.
+ struct ConsumerListener : 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;
+
+ // onBuffersReleased is called to notify the buffer consumer that the
+ // GonkBufferQueue has released its references to one or more GraphicBuffers
+ // contained in its slots. The buffer consumer should then call
+ // GonkBufferQueue::getReleasedBuffers to retrieve the list of buffers
+ //
+ // This is called without any lock held and can be called concurrently
+ // by multiple threads.
+ virtual void onBuffersReleased() = 0;
+ };
+
+ // ProxyConsumerListener is a ConsumerListener implementation that keeps a weak
+ // reference to the actual consumer object. It forwards all calls to that
+ // consumer object so long as it exists.
+ //
+ // This class exists to avoid having a circular reference between the
+ // GonkBufferQueue object and the consumer object. The reason this can't be a weak
+ // reference in the GonkBufferQueue class is because we're planning to expose the
+ // consumer side of a GonkBufferQueue as a binder interface, which doesn't support
+ // weak references.
+ class ProxyConsumerListener : public GonkBufferQueue::ConsumerListener {
+ public:
+
+ ProxyConsumerListener(const wp<GonkBufferQueue::ConsumerListener>& consumerListener);
+ virtual ~ProxyConsumerListener();
+ virtual void onFrameAvailable();
+ virtual void onBuffersReleased();
+
+ private:
+
+ // mConsumerListener is a weak reference to the ConsumerListener. This is
+ // the raison d'etre of ProxyConsumerListener.
+ wp<GonkBufferQueue::ConsumerListener> mConsumerListener;
+ };
+
+
+ // GonkBufferQueue manages a pool of gralloc memory slots to be used by
+ // producers and consumers. allowSynchronousMode specifies whether or not
+ // synchronous mode can be enabled by the producer. allocator is used to
+ // allocate all the needed gralloc buffers.
+ GonkBufferQueue(bool allowSynchronousMode = true,
+ const sp<IGraphicBufferAlloc>& allocator = NULL);
+ virtual ~GonkBufferQueue();
+
+ // Query native window attributes. The "what" values are enumerated in
+ // window.h (e.g. NATIVE_WINDOW_FORMAT).
+ virtual int query(int what, int* value);
+
+ // setBufferCount updates the number of available buffer slots. If this
+ // method succeeds, buffer slots will be both unallocated and owned by
+ // the GonkBufferQueue object (i.e. they are not owned by the producer or
+ // consumer).
+ //
+ // This will fail if the producer has dequeued any buffers, or if
+ // bufferCount is invalid. bufferCount must generally be a value
+ // between the minimum undequeued buffer count and NUM_BUFFER_SLOTS
+ // (inclusive). It may also be set to zero (the default) to indicate
+ // that the producer does not wish to set a value. The minimum value
+ // can be obtained by calling query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+ // ...).
+ //
+ // This may only be called by the producer. The consumer will be told
+ // to discard buffers through the onBuffersReleased callback.
+ virtual status_t setBufferCount(int bufferCount);
+
+ // requestBuffer returns the GraphicBuffer for slot N.
+ //
+ // In normal operation, this is called the first time slot N is returned
+ // by dequeueBuffer. It must be called again if dequeueBuffer returns
+ // flags indicating that previously-returned buffers are no longer valid.
+ virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf);
+
+ // dequeueBuffer gets the next buffer slot index for the producer 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 fence parameter will be updated to hold the fence associated with
+ // the buffer. The contents of the buffer must not be overwritten until the
+ // fence signals. If the fence is Fence::NO_FENCE, the buffer may be
+ // written immediately.
+ //
+ // 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. If width and height are both zero, the
+ // default values specified by setDefaultBufferSize() are used instead.
+ //
+ // The pixel formats are enumerated in graphics.h, e.g.
+ // HAL_PIXEL_FORMAT_RGBA_8888. If the format is 0, the default format
+ // will be used.
+ //
+ // The usage argument specifies gralloc buffer usage flags. The values
+ // are enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER. These
+ // will be merged with the usage flags specified by setConsumerUsageBits.
+ //
+ // The return value may be a negative error value or a non-negative
+ // collection of flags. If the flags are set, the return values are
+ // valid, but additional actions must be performed.
+ //
+ // If IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION is set, the
+ // producer must discard cached GraphicBuffer references for the slot
+ // returned in buf.
+ // If IGraphicBufferProducer::RELEASE_ALL_BUFFERS is set, the producer
+ // must discard cached GraphicBuffer references for all slots.
+ //
+ // In both cases, the producer will need to call requestBuffer to get a
+ // GraphicBuffer handle for the returned slot.
+#if ANDROID_VERSION == 17
+ virtual status_t dequeueBuffer(int *buf, sp<Fence>& fence,
+ uint32_t width, uint32_t height, uint32_t format, uint32_t usage) {
+ return dequeueBuffer(buf, &fence, width, height, format, usage);
+ }
+#endif
+
+ virtual status_t dequeueBuffer(int *buf, sp<Fence>* fence,
+ uint32_t width, uint32_t height, uint32_t format, uint32_t usage);
+
+ // queueBuffer returns a filled buffer to the GonkBufferQueue.
+ //
+ // Additional data is provided in the QueueBufferInput struct. Notably,
+ // 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 producer-specific and should be documented by the
+ // producer.
+ //
+ // The caller may provide a fence that signals when all rendering
+ // operations have completed. Alternatively, NO_FENCE may be used,
+ // indicating that the buffer is ready immediately.
+ //
+ // Some values are returned in the output struct: the current settings
+ // for default width and height, the current transform hint, and the
+ // number of queued buffers.
+ virtual status_t queueBuffer(int buf,
+ const QueueBufferInput& input, QueueBufferOutput* output);
+
+ // cancelBuffer returns a dequeued buffer to the GonkBufferQueue, but doesn't
+ // queue it for use by the consumer.
+ //
+ // The buffer will not be overwritten until the fence signals. The fence
+ // will usually be the one obtained from dequeueBuffer.
+#if ANDROID_VERSION == 17
+ virtual void cancelBuffer(int buf, sp<Fence> fence);
+#else
+ virtual void cancelBuffer(int buf, const sp<Fence>& fence);
+#endif
+
+ // setSynchronousMode sets 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 acquired in order. In asynchronous mode,
+ // a queued buffer may be replaced by a subsequently queued buffer.
+ //
+ // The default mode is synchronous.
+ // This should be called only during initialization.
+ virtual status_t setSynchronousMode(bool enabled);
+
+ // connect attempts to connect a producer API to the GonkBufferQueue. This
+ // must be called before any other IGraphicBufferProducer methods are
+ // called except for getAllocator. A consumer must already be connected.
+ //
+ // This method will fail if connect was previously called on the
+ // GonkBufferQueue and no corresponding disconnect call was made (i.e. if
+ // it's still connected to a producer).
+ //
+ // APIs are enumerated in window.h (e.g. NATIVE_WINDOW_API_CPU).
+ virtual status_t connect(int api, QueueBufferOutput* output);
+
+ // disconnect attempts to disconnect a producer API from the GonkBufferQueue.
+ // Calling this method will cause any subsequent calls to other
+ // IGraphicBufferProducer 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 GonkBufferQueue is not currently
+ // connected to the specified producer API.
+ virtual status_t disconnect(int api);
+
+ // dump our state in a String
+ virtual void dumpToString(String8& result) const;
+ virtual void dumpToString(String8& result, const char* prefix, char* buffer, size_t SIZE) const;
+
+ // public facing structure for BufferSlot
+ struct BufferItem {
+
+ BufferItem()
+ :
+ mTransform(0),
+ mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+ mTimestamp(0),
+ mFrameNumber(0),
+ mBuf(INVALID_BUFFER_SLOT) {
+ mCrop.makeInvalid();
+ }
+ // mGraphicBuffer points to the buffer allocated for this slot, or is NULL
+ // if the buffer in this slot has been acquired in the past (see
+ // BufferSlot.mAcquireCalled).
+ sp<GraphicBuffer> mGraphicBuffer;
+
+ // mCrop is the current crop rectangle for this buffer slot.
+ Rect mCrop;
+
+ // mTransform is the current transform flags for this buffer slot.
+ uint32_t mTransform;
+
+ // mScalingMode is the current scaling mode for this buffer 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;
+
+ // mBuf is the slot index of this buffer
+ int mBuf;
+
+ // mFence is a fence that will signal when the buffer is idle.
+ sp<Fence> mFence;
+ };
+
+ // The following public functions are the consumer-facing interface
+
+ // acquireBuffer attempts to acquire ownership of the next pending buffer in
+ // the GonkBufferQueue. If no buffer is pending then it returns -EINVAL. If a
+ // buffer is successfully acquired, the information about the buffer is
+ // returned in BufferItem. If the buffer returned had previously been
+ // acquired then the BufferItem::mGraphicBuffer field of buffer is set to
+ // NULL and it is assumed that the consumer still holds a reference to the
+ // buffer.
+ status_t acquireBuffer(BufferItem *buffer);
+
+ // releaseBuffer releases a buffer slot from the consumer back to the
+ // GonkBufferQueue. This may be done while the buffer's contents are still
+ // being accessed. The fence will signal when the buffer is no longer
+ // in use.
+ //
+ // If releaseBuffer returns STALE_BUFFER_SLOT, then the consumer must free
+ // any references to the just-released buffer that it might have, as if it
+ // had received a onBuffersReleased() call with a mask set for the released
+ // buffer.
+ //
+ // Note that the dependencies on EGL will be removed once we switch to using
+ // the Android HW Sync HAL.
+ status_t releaseBuffer(int buf, const sp<Fence>& releaseFence);
+
+ // consumerConnect connects a consumer to the GonkBufferQueue. Only one
+ // consumer may be connected, and when that consumer disconnects the
+ // GonkBufferQueue is placed into the "abandoned" state, causing most
+ // interactions with the GonkBufferQueue by the producer to fail.
+ //
+ // consumer may not be NULL.
+ status_t consumerConnect(const sp<ConsumerListener>& consumer);
+
+ // consumerDisconnect disconnects a consumer from the GonkBufferQueue. All
+ // buffers will be freed and the GonkBufferQueue is placed in the "abandoned"
+ // state, causing most interactions with the GonkBufferQueue by the producer to
+ // fail.
+ status_t consumerDisconnect();
+
+ // getReleasedBuffers sets the value pointed to by slotMask to a bit mask
+ // indicating which buffer slots have been released by the GonkBufferQueue
+ // but have not yet been released by the consumer.
+ //
+ // This should be called from the onBuffersReleased() callback.
+ status_t getReleasedBuffers(uint32_t* slotMask);
+
+ // setDefaultBufferSize is used to set the size of buffers returned by
+ // dequeueBuffer when a width and height of zero is requested. Default
+ // is 1x1.
+ status_t setDefaultBufferSize(uint32_t w, uint32_t h);
+
+ // setDefaultMaxBufferCount sets the default value for the maximum buffer
+ // count (the initial default is 2). If the producer has requested a
+ // buffer count using setBufferCount, the default buffer count will only
+ // take effect if the producer sets the count back to zero.
+ //
+ // The count must be between 2 and NUM_BUFFER_SLOTS, inclusive.
+ status_t setDefaultMaxBufferCount(int bufferCount);
+
+ // setMaxAcquiredBufferCount sets the maximum number of buffers that can
+ // be acquired by the consumer at one time (default 1). This call will
+ // fail if a producer is connected to the GonkBufferQueue.
+ status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers);
+
+ // isSynchronousMode returns whether the GonkBufferQueue is currently in
+ // synchronous mode.
+ bool isSynchronousMode() const;
+
+ // setConsumerName sets the name used in logging
+ void setConsumerName(const String8& name);
+
+ // setDefaultBufferFormat allows the GonkBufferQueue to create
+ // GraphicBuffers of a defaultFormat if no format is specified
+ // in dequeueBuffer. Formats are enumerated in graphics.h; the
+ // initial default is HAL_PIXEL_FORMAT_RGBA_8888.
+ status_t setDefaultBufferFormat(uint32_t defaultFormat);
+
+ // setConsumerUsageBits will turn on additional usage bits for dequeueBuffer.
+ // These are merged with the bits passed to dequeueBuffer. The values are
+ // enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER; the default is 0.
+ status_t setConsumerUsageBits(uint32_t usage);
+
+ // setTransformHint bakes in rotation to buffers so overlays can be used.
+ // The values are enumerated in window.h, e.g.
+ // NATIVE_WINDOW_TRANSFORM_ROT_90. The default is 0 (no transform).
+ status_t setTransformHint(uint32_t hint);
+
+ already_AddRefed<TextureClient> getTextureClientFromBuffer(ANativeWindowBuffer* buffer);
+
+ int getSlotFromTextureClientLocked(TextureClient* client) const;
+
+private:
+ // freeBufferLocked frees the GraphicBuffer and sync resources for the
+ // given slot.
+ //void freeBufferLocked(int index);
+
+ // freeAllBuffersLocked frees the GraphicBuffer and sync resources for
+ // all slots.
+ //void freeAllBuffersLocked();
+ void freeAllBuffersLocked();
+
+ // setDefaultMaxBufferCountLocked sets the maximum number of buffer slots
+ // that will be used if the producer does not override the buffer slot
+ // count. The count must be between 2 and NUM_BUFFER_SLOTS, inclusive.
+ // The initial default is 2.
+ status_t setDefaultMaxBufferCountLocked(int count);
+
+ // getMinBufferCountLocked returns the minimum number of buffers allowed
+ // given the current GonkBufferQueue state.
+ int getMinMaxBufferCountLocked() const;
+
+ // getMinUndequeuedBufferCountLocked returns the minimum number of buffers
+ // that must remain in a state other than DEQUEUED.
+ int getMinUndequeuedBufferCountLocked() const;
+
+ // getMaxBufferCountLocked returns the maximum number of buffers that can
+ // be allocated at once. This value depends upon the following member
+ // variables:
+ //
+ // mSynchronousMode
+ // mMaxAcquiredBufferCount
+ // mDefaultMaxBufferCount
+ // mOverrideMaxBufferCount
+ //
+ // Any time one of these member variables is changed while a producer is
+ // connected, mDequeueCondition must be broadcast.
+ int getMaxBufferCountLocked() const;
+
+ struct BufferSlot {
+
+ BufferSlot()
+ : mBufferState(BufferSlot::FREE),
+ mRequestBufferCalled(false),
+ mTransform(0),
+ mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+ mTimestamp(0),
+ mFrameNumber(0),
+ mAcquireCalled(false),
+ mNeedsCleanupOnRelease(false) {
+ mCrop.makeInvalid();
+ }
+
+ // mGraphicBuffer points to the buffer allocated for this slot or is NULL
+ // if no buffer has been allocated.
+ sp<GraphicBuffer> mGraphicBuffer;
+
+ // mTextureClient is a thin abstraction over remotely allocated GraphicBuffer.
+ RefPtr<TextureClient> mTextureClient;
+
+ // BufferState represents the different states in which a buffer slot
+ // can be. All slots are initially FREE.
+ enum BufferState {
+ // FREE indicates that the buffer is available to be dequeued
+ // by the producer. The buffer may be in use by the consumer for
+ // a finite time, so the buffer must not be modified until the
+ // associated fence is signaled.
+ //
+ // The slot is "owned" by GonkBufferQueue. It transitions to DEQUEUED
+ // when dequeueBuffer is called.
+ FREE = 0,
+
+ // DEQUEUED indicates that the buffer has been dequeued by the
+ // producer, but has not yet been queued or canceled. The
+ // producer may modify the buffer's contents as soon as the
+ // associated ready fence is signaled.
+ //
+ // The slot is "owned" by the producer. It can transition to
+ // QUEUED (via queueBuffer) or back to FREE (via cancelBuffer).
+ DEQUEUED = 1,
+
+ // QUEUED indicates that the buffer has been filled by the
+ // producer and queued for use by the consumer. The buffer
+ // contents may continue to be modified for a finite time, so
+ // the contents must not be accessed until the associated fence
+ // is signaled.
+ //
+ // The slot is "owned" by GonkBufferQueue. It can transition to
+ // ACQUIRED (via acquireBuffer) or to FREE (if another buffer is
+ // queued in asynchronous mode).
+ QUEUED = 2,
+
+ // ACQUIRED indicates that the buffer has been acquired by the
+ // consumer. As with QUEUED, the contents must not be accessed
+ // by the consumer until the fence is signaled.
+ //
+ // The slot is "owned" by the consumer. It transitions to FREE
+ // when releaseBuffer is called.
+ ACQUIRED = 3
+ };
+
+ // mBufferState is the current state of this buffer slot.
+ BufferState mBufferState;
+
+ // mRequestBufferCalled is used for validating that the producer did
+ // call requestBuffer() when told to do so. Technically this is not
+ // needed but useful for debugging and catching producer bugs.
+ bool mRequestBufferCalled;
+
+ // mCrop is the current crop rectangle for this buffer slot.
+ Rect mCrop;
+
+ // mTransform is the current transform flags for this buffer slot.
+ // (example: NATIVE_WINDOW_TRANSFORM_ROT_90)
+ uint32_t mTransform;
+
+ // mScalingMode is the current scaling mode for this buffer slot.
+ // (example: NATIVE_WINDOW_SCALING_MODE_FREEZE)
+ 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. This
+ // is used to dequeue buffers in LRU order (useful because buffers
+ // may be released before their release fence is signaled).
+ uint64_t mFrameNumber;
+
+ // mEglFence 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 may be set to a
+ // new sync object in releaseBuffer. (This is deprecated in favor of
+ // mFence, below.)
+ //EGLSyncKHR mEglFence;
+
+ // mFence is a fence which will signal when work initiated by the
+ // previous owner of the buffer is finished. When the buffer is FREE,
+ // the fence indicates when the consumer has finished reading
+ // from the buffer, or when the producer has finished writing if it
+ // called cancelBuffer after queueing some writes. When the buffer is
+ // QUEUED, it indicates when the producer has finished filling the
+ // buffer. When the buffer is DEQUEUED or ACQUIRED, the fence has been
+ // passed to the consumer or producer along with ownership of the
+ // buffer, and mFence is set to NO_FENCE.
+ sp<Fence> mFence;
+
+ // Indicates whether this buffer has been seen by a consumer yet
+ bool mAcquireCalled;
+
+ // Indicates whether this buffer needs to be cleaned up by the
+ // consumer. This is set when a buffer in ACQUIRED state is freed.
+ // It causes releaseBuffer to return STALE_BUFFER_SLOT.
+ bool mNeedsCleanupOnRelease;
+ };
+
+ // mSlots is the array of buffer slots that must be mirrored on the
+ // producer side. This allows buffer ownership to be transferred between
+ // the producer and consumer 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 dequeueBuffer() if a width and height of zero is specified.
+ uint32_t mDefaultWidth;
+
+ // mDefaultHeight holds the default height of allocated buffers. It is used
+ // in dequeueBuffer() if a width and height of zero is specified.
+ uint32_t mDefaultHeight;
+
+ // mMaxAcquiredBufferCount is the number of buffers that the consumer may
+ // acquire at one time. It defaults to 1 and can be changed by the
+ // consumer via the setMaxAcquiredBufferCount method, but this may only be
+ // done when no producer is connected to the GonkBufferQueue.
+ //
+ // This value is used to derive the value returned for the
+ // MIN_UNDEQUEUED_BUFFERS query by the producer.
+ int mMaxAcquiredBufferCount;
+
+ // mDefaultMaxBufferCount is the default limit on the number of buffers
+ // that will be allocated at one time. This default limit is set by the
+ // consumer. The limit (as opposed to the default limit) may be
+ // overridden by the producer.
+ int mDefaultMaxBufferCount;
+
+ // mOverrideMaxBufferCount is the limit on the number of buffers that will
+ // be allocated at one time. This value is set by the image producer by
+ // calling setBufferCount. The default is zero, which means the producer
+ // doesn't care about the number of buffers in the pool. In that case
+ // mDefaultMaxBufferCount is used as the limit.
+ int mOverrideMaxBufferCount;
+
+ // mGraphicBufferAlloc is the connection to SurfaceFlinger that is used to
+ // allocate new GraphicBuffer objects.
+ sp<IGraphicBufferAlloc> mGraphicBufferAlloc;
+
+ // mConsumerListener is used to notify the connected consumer of
+ // asynchronous events that it may wish to react to. It is initially set
+ // to NULL and is written by consumerConnect and consumerDisconnect.
+ sp<ConsumerListener> mConsumerListener;
+
+ // mSynchronousMode whether we're in synchronous mode or not
+ bool mSynchronousMode;
+
+ // mAllowSynchronousMode whether we allow synchronous mode or not. Set
+ // when the GonkBufferQueue is created (by the consumer).
+ const bool mAllowSynchronousMode;
+
+ // mConnectedApi indicates the producer API that is currently connected
+ // to this GonkBufferQueue. 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 GonkBufferQueue will no longer be used to
+ // consume image buffers pushed to it using the IGraphicBufferProducer
+ // interface. It is initialized to false, and set to true in the
+ // consumerDisconnect method. A GonkBufferQueue that has been abandoned will
+ // return the NO_INIT error from all IGraphicBufferProducer methods
+ // capable of returning an error.
+ bool mAbandoned;
+
+ // mConsumerName is a string used to identify the GonkBufferQueue in log
+ // messages. It is set by the setConsumerName method.
+ String8 mConsumerName;
+
+ // mMutex is the mutex used to prevent concurrent access to the member
+ // variables of GonkBufferQueue objects. It must be locked whenever the
+ // member variables are accessed.
+ mutable Mutex mMutex;
+
+ // mFrameCounter is the free running counter, incremented on every
+ // successful queueBuffer call.
+ uint64_t mFrameCounter;
+
+ // mBufferHasBeenQueued is true once a buffer has been queued. It is
+ // reset when something causes all buffers to be freed (e.g. changing the
+ // buffer count).
+ bool mBufferHasBeenQueued;
+
+ // mDefaultBufferFormat can be set so it will override
+ // the buffer format when it isn't specified in dequeueBuffer
+ uint32_t mDefaultBufferFormat;
+
+ // mConsumerUsageBits contains flags the consumer wants for GraphicBuffers
+ uint32_t mConsumerUsageBits;
+
+ // mTransformHint is used to optimize for screen rotations
+ uint32_t mTransformHint;
+
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_GUI_BUFFERQUEUE_H
diff --git a/widget/gonk/nativewindow/GonkBufferQueueKK.cpp b/widget/gonk/nativewindow/GonkBufferQueueKK.cpp
new file mode 100644
index 000000000..0c5cdfeb9
--- /dev/null
+++ b/widget/gonk/nativewindow/GonkBufferQueueKK.cpp
@@ -0,0 +1,1265 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2013 Mozilla Foundation
+ *
+ * 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 "GonkBufferQueue"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+//#define LOG_NDEBUG 0
+
+#define GL_GLEXT_PROTOTYPES
+#define EGL_EGLEXT_PROTOTYPES
+
+#include <utils/Log.h>
+#include <utils/Trace.h>
+#include <utils/CallStack.h>
+#include <cutils/compiler.h>
+
+#include "mozilla/layers/GrallocTextureClient.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+#include "GonkBufferQueueKK.h"
+
+#define ATRACE_BUFFER_INDEX(index)
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+
+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);
+}
+
+static const char* scalingModeName(int scalingMode) {
+ switch (scalingMode) {
+ case NATIVE_WINDOW_SCALING_MODE_FREEZE: return "FREEZE";
+ case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: return "SCALE_TO_WINDOW";
+ case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP: return "SCALE_CROP";
+ default: return "Unknown";
+ }
+}
+
+GonkBufferQueue::GonkBufferQueue(bool allowSynchronousMode,
+ const sp<IGraphicBufferAlloc>& allocator) :
+ mDefaultWidth(1),
+ mDefaultHeight(1),
+ mMaxAcquiredBufferCount(1),
+ mDefaultMaxBufferCount(2),
+ mOverrideMaxBufferCount(0),
+ mSynchronousMode(true),
+ mConsumerControlledByApp(false),
+ mDequeueBufferCannotBlock(false),
+ mUseAsyncBuffer(true),
+ mConnectedApi(NO_CONNECTED_API),
+ mAbandoned(false),
+ mFrameCounter(0),
+ mBufferHasBeenQueued(false),
+ mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888),
+ mConsumerUsageBits(0),
+ mTransformHint(0)
+{
+ // Choose a name using the PID and a process-unique ID.
+ mConsumerName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
+
+ ALOGV("GonkBufferQueue");
+}
+
+GonkBufferQueue::~GonkBufferQueue() {
+ ALOGV("~GonkBufferQueue");
+}
+
+status_t GonkBufferQueue::setDefaultMaxBufferCountLocked(int count) {
+ if (count < 2 || count > NUM_BUFFER_SLOTS)
+ return BAD_VALUE;
+
+ mDefaultMaxBufferCount = count;
+ mDequeueCondition.broadcast();
+
+ return NO_ERROR;
+}
+
+void GonkBufferQueue::setConsumerName(const String8& name) {
+ Mutex::Autolock lock(mMutex);
+ mConsumerName = name;
+}
+
+status_t GonkBufferQueue::setDefaultBufferFormat(uint32_t defaultFormat) {
+ Mutex::Autolock lock(mMutex);
+ mDefaultBufferFormat = defaultFormat;
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueue::setConsumerUsageBits(uint32_t usage) {
+ Mutex::Autolock lock(mMutex);
+ mConsumerUsageBits = usage;
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueue::setTransformHint(uint32_t hint) {
+ ALOGV("setTransformHint: %02x", hint);
+ Mutex::Autolock lock(mMutex);
+ mTransformHint = hint;
+ return NO_ERROR;
+}
+
+already_AddRefed<TextureClient>
+GonkBufferQueue::getTextureClientFromBuffer(ANativeWindowBuffer* buffer)
+{
+ Mutex::Autolock _l(mMutex);
+ if (buffer == NULL) {
+ ALOGE("getSlotFromBufferLocked: encountered NULL buffer");
+ return nullptr;
+ }
+
+ for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
+ if (mSlots[i].mGraphicBuffer != NULL && mSlots[i].mGraphicBuffer->handle == buffer->handle) {
+ RefPtr<TextureClient> client(mSlots[i].mTextureClient);
+ return client.forget();
+ }
+ }
+ ALOGE("getSlotFromBufferLocked: unknown buffer: %p", buffer->handle);
+ return nullptr;
+}
+
+int GonkBufferQueue::getSlotFromTextureClientLocked(
+ TextureClient* client) const
+{
+ if (client == NULL) {
+ ALOGE("getSlotFromBufferLocked: encountered NULL buffer");
+ return BAD_VALUE;
+ }
+
+ for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
+ if (mSlots[i].mTextureClient == client) {
+ return i;
+ }
+ }
+ ALOGE("getSlotFromBufferLocked: unknown TextureClient: %p", client);
+ return BAD_VALUE;
+}
+
+status_t GonkBufferQueue::setBufferCount(int bufferCount) {
+ ALOGV("setBufferCount: count=%d", bufferCount);
+
+ sp<IConsumerListener> listener;
+ {
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ ALOGE("setBufferCount: GonkBufferQueue has been abandoned!");
+ return NO_INIT;
+ }
+ if (bufferCount > NUM_BUFFER_SLOTS) {
+ ALOGE("setBufferCount: bufferCount too large (max %d)",
+ NUM_BUFFER_SLOTS);
+ return BAD_VALUE;
+ }
+
+ // Error out if the user has dequeued buffers
+ for (int i=0 ; i<NUM_BUFFER_SLOTS; i++) {
+ if (mSlots[i].mBufferState == BufferSlot::DEQUEUED) {
+ ALOGE("setBufferCount: client owns some buffers");
+ return -EINVAL;
+ }
+ }
+
+ if (bufferCount == 0) {
+ mOverrideMaxBufferCount = 0;
+ mDequeueCondition.broadcast();
+ return NO_ERROR;
+ }
+
+ // fine to assume async to false before we're setting the buffer count
+ const int minBufferSlots = getMinMaxBufferCountLocked(false);
+ if (bufferCount < minBufferSlots) {
+ ALOGE("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. We don't clear the
+ // queue, however, so currently queued buffers still get displayed.
+ // XXX: Should this use drainQueueAndFreeBuffersLocked instead?
+ freeAllBuffersLocked();
+ mOverrideMaxBufferCount = bufferCount;
+ mDequeueCondition.broadcast();
+ listener = mConsumerListener;
+ } // scope for lock
+
+ if (listener != NULL) {
+ listener->onBuffersReleased();
+ }
+
+ return NO_ERROR;
+}
+
+int GonkBufferQueue::query(int what, int* outValue)
+{
+ ATRACE_CALL();
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ ALOGE("query: GonkBufferQueue has been abandoned!");
+ return NO_INIT;
+ }
+
+ int value;
+ switch (what) {
+ case NATIVE_WINDOW_WIDTH:
+ value = mDefaultWidth;
+ break;
+ case NATIVE_WINDOW_HEIGHT:
+ value = mDefaultHeight;
+ break;
+ case NATIVE_WINDOW_FORMAT:
+ value = mDefaultBufferFormat;
+ break;
+ case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
+ value = getMinUndequeuedBufferCount(false);
+ break;
+ case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND:
+ value = (mQueue.size() >= 2);
+ break;
+ case NATIVE_WINDOW_CONSUMER_USAGE_BITS:
+ value = mConsumerUsageBits;
+ break;
+ default:
+ return BAD_VALUE;
+ }
+ outValue[0] = value;
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueue::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
+ ATRACE_CALL();
+ ALOGV("requestBuffer: slot=%d", slot);
+ Mutex::Autolock lock(mMutex);
+ if (mAbandoned) {
+ ALOGE("requestBuffer: GonkBufferQueue has been abandoned!");
+ return NO_INIT;
+ }
+ if (slot < 0 || slot >= NUM_BUFFER_SLOTS) {
+ ALOGE("requestBuffer: slot index out of range [0, %d]: %d",
+ NUM_BUFFER_SLOTS, slot);
+ return BAD_VALUE;
+ } else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) {
+ ALOGE("requestBuffer: slot %d is not owned by the client (state=%d)",
+ slot, mSlots[slot].mBufferState);
+ return BAD_VALUE;
+ }
+ mSlots[slot].mRequestBufferCalled = true;
+ *buf = mSlots[slot].mGraphicBuffer;
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueue::dequeueBuffer(int *outBuf, sp<Fence>* outFence, bool async,
+ uint32_t w, uint32_t h, uint32_t format, uint32_t usage) {
+ ATRACE_CALL();
+ ALOGV("dequeueBuffer: w=%d h=%d fmt=%#x usage=%#x", w, h, format, usage);
+
+ if ((w && !h) || (!w && h)) {
+ ALOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h);
+ return BAD_VALUE;
+ }
+
+ status_t returnFlags(OK);
+ int buf = INVALID_BUFFER_SLOT;
+
+ { // Scope for the lock
+ Mutex::Autolock lock(mMutex);
+
+ if (format == 0) {
+ format = mDefaultBufferFormat;
+ }
+ // turn on usage bits the consumer requested
+ usage |= mConsumerUsageBits;
+
+ int found = -1;
+ bool tryAgain = true;
+ while (tryAgain) {
+ if (mAbandoned) {
+ ALOGE("dequeueBuffer: GonkBufferQueue has been abandoned!");
+ return NO_INIT;
+ }
+
+ const int maxBufferCount = getMaxBufferCountLocked(async);
+ if (async && mOverrideMaxBufferCount) {
+ // FIXME: some drivers are manually setting the buffer-count (which they
+ // shouldn't), so we do this extra test here to handle that case.
+ // This is TEMPORARY, until we get this fixed.
+ if (mOverrideMaxBufferCount < maxBufferCount) {
+ ALOGE("dequeueBuffer: async mode is invalid with buffercount override");
+ return BAD_VALUE;
+ }
+ }
+
+ // Free up any buffers that are in slots beyond the max buffer
+ // count.
+ //for (int i = maxBufferCount; i < NUM_BUFFER_SLOTS; i++) {
+ // assert(mSlots[i].mBufferState == BufferSlot::FREE);
+ // if (mSlots[i].mGraphicBuffer != NULL) {
+ // freeBufferLocked(i);
+ // returnFlags |= IGraphicBufferProducer::RELEASE_ALL_BUFFERS;
+ // }
+ //}
+
+ // look for a free buffer to give to the client
+ found = INVALID_BUFFER_SLOT;
+ int dequeuedCount = 0;
+ int acquiredCount = 0;
+ for (int i = 0; i < maxBufferCount; i++) {
+ const int state = mSlots[i].mBufferState;
+ switch (state) {
+ case BufferSlot::DEQUEUED:
+ dequeuedCount++;
+ break;
+ case BufferSlot::ACQUIRED:
+ acquiredCount++;
+ break;
+ case 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.
+ */
+ if ((found < 0) ||
+ mSlots[i].mFrameNumber < mSlots[found].mFrameNumber) {
+ found = i;
+ }
+ break;
+ }
+ }
+
+ // clients are not allowed to dequeue more than one buffer
+ // if they didn't set a buffer count.
+ if (!mOverrideMaxBufferCount && dequeuedCount) {
+ ALOGE("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.
+ if (mBufferHasBeenQueued) {
+ // make sure the client is not trying to dequeue more buffers
+ // than allowed.
+ const int newUndequeuedCount = maxBufferCount - (dequeuedCount+1);
+ const int minUndequeuedCount = getMinUndequeuedBufferCount(async);
+ if (newUndequeuedCount < minUndequeuedCount) {
+ ALOGE("dequeueBuffer: min undequeued buffer count (%d) "
+ "exceeded (dequeued=%d undequeudCount=%d)",
+ minUndequeuedCount, dequeuedCount,
+ newUndequeuedCount);
+ return -EBUSY;
+ }
+ }
+
+ // If no buffer is found, wait for a buffer to be released or for
+ // the max buffer count to change.
+ tryAgain = found == INVALID_BUFFER_SLOT;
+ if (tryAgain) {
+ // return an error if we're in "cannot block" mode (producer and consumer
+ // are controlled by the application) -- however, the consumer is allowed
+ // to acquire briefly an extra buffer (which could cause us to have to wait here)
+ // and that's okay because we know the wait will be brief (it happens
+ // if we dequeue a buffer while the consumer has acquired one but not released
+ // the old one yet -- for e.g.: see GLConsumer::updateTexImage()).
+ if (mDequeueBufferCannotBlock && (acquiredCount <= mMaxAcquiredBufferCount)) {
+ ALOGE("dequeueBuffer: would block! returning an error instead.");
+ return WOULD_BLOCK;
+ }
+ mDequeueCondition.wait(mMutex);
+ }
+ }
+
+
+ if (found == INVALID_BUFFER_SLOT) {
+ // This should not happen.
+ ALOGE("dequeueBuffer: no available buffer slots");
+ return -EBUSY;
+ }
+
+ buf = found;
+ *outBuf = found;
+
+ const bool useDefaultSize = !w && !h;
+ if (useDefaultSize) {
+ // use the default size
+ w = mDefaultWidth;
+ h = mDefaultHeight;
+ }
+
+ 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))
+ {
+ mSlots[buf].mAcquireCalled = false;
+ mSlots[buf].mGraphicBuffer = NULL;
+ mSlots[buf].mRequestBufferCalled = false;
+ mSlots[buf].mFence = Fence::NO_FENCE;
+ if (mSlots[buf].mTextureClient) {
+ mSlots[buf].mTextureClient->ClearRecycleCallback();
+ // release TextureClient in ImageBridge thread
+ RefPtr<TextureClientReleaseTask> task =
+ MakeAndAddRef<TextureClientReleaseTask>(mSlots[buf].mTextureClient);
+ mSlots[buf].mTextureClient = NULL;
+ ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(task.forget());
+ }
+ returnFlags |= IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION;
+ }
+
+
+ if (CC_UNLIKELY(mSlots[buf].mFence == NULL)) {
+ ALOGE("dequeueBuffer: about to return a NULL fence from mSlot. "
+ "buf=%d, w=%d, h=%d, format=%d",
+ buf, buffer->width, buffer->height, buffer->format);
+ }
+ *outFence = mSlots[buf].mFence;
+ mSlots[buf].mFence = Fence::NO_FENCE;
+ } // end lock scope
+
+ if (returnFlags & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) {
+
+ RefPtr<LayersIPCChannel> allocator = ImageBridgeChild::GetSingleton();
+ usage |= GraphicBuffer::USAGE_HW_TEXTURE;
+ GrallocTextureData* texData = GrallocTextureData::Create(IntSize(w, h), format,
+ gfx::BackendType::NONE, usage,
+ allocator);
+ if (!texData) {
+ return -ENOMEM;
+ }
+
+ RefPtr<TextureClient> textureClient = new TextureClient(texData, TextureFlags::RECYCLE | TextureFlags::DEALLOCATE_CLIENT, allocator);
+
+ { // Scope for the lock
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ ALOGE("dequeueBuffer: SurfaceTexture has been abandoned!");
+ return NO_INIT;
+ }
+
+ mSlots[buf].mGraphicBuffer = texData->GetGraphicBuffer();
+ mSlots[buf].mTextureClient = textureClient;
+ ALOGD("dequeueBuffer: returning slot=%d buf=%p ", buf,
+ mSlots[buf].mGraphicBuffer->handle);
+
+ }
+
+ }
+
+ ALOGV("dequeueBuffer: returning slot=%d/%llu buf=%p flags=%#x", *outBuf,
+ mSlots[*outBuf].mFrameNumber,
+ mSlots[*outBuf].mGraphicBuffer->handle, returnFlags);
+
+ return returnFlags;
+}
+
+status_t GonkBufferQueue::setSynchronousMode(bool enabled) {
+ ALOGV("setSynchronousMode: enabled=%d", enabled);
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ ALOGE("setSynchronousMode: BufferQueue has been abandoned!");
+ return NO_INIT;
+ }
+
+ if (mSynchronousMode != enabled) {
+ mSynchronousMode = enabled;
+ mDequeueCondition.broadcast();
+ }
+ return OK;
+}
+
+status_t GonkBufferQueue::queueBuffer(int buf,
+ const QueueBufferInput& input, QueueBufferOutput* output) {
+ ATRACE_CALL();
+
+ Rect crop;
+ uint32_t transform;
+ int scalingMode;
+ int64_t timestamp;
+ bool isAutoTimestamp;
+ bool async;
+ sp<Fence> fence;
+
+ input.deflate(&timestamp, &isAutoTimestamp, &crop, &scalingMode, &transform,
+ &async, &fence);
+
+ if (fence == NULL) {
+ ALOGE("queueBuffer: fence is NULL");
+ return BAD_VALUE;
+ }
+
+ ALOGV("queueBuffer: slot=%d time=%#llx crop=[%d,%d,%d,%d] tr=%#x "
+ "scale=%s",
+ buf, timestamp, crop.left, crop.top, crop.right, crop.bottom,
+ transform, scalingModeName(scalingMode));
+
+ switch (scalingMode) {
+ case NATIVE_WINDOW_SCALING_MODE_FREEZE:
+ case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
+ case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP:
+ case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP:
+ break;
+ default:
+ ALOGE("unknown scaling mode: %d", scalingMode);
+ return -EINVAL;
+ }
+
+ sp<IConsumerListener> listener;
+
+ { // scope for the lock
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ ALOGE("queueBuffer: GonkBufferQueue has been abandoned!");
+ return NO_INIT;
+ }
+
+ const int maxBufferCount = getMaxBufferCountLocked(async);
+ if (async && mOverrideMaxBufferCount) {
+ // FIXME: some drivers are manually setting the buffer-count (which they
+ // shouldn't), so we do this extra test here to handle that case.
+ // This is TEMPORARY, until we get this fixed.
+ if (mOverrideMaxBufferCount < maxBufferCount) {
+ ALOGE("queueBuffer: async mode is invalid with buffercount override");
+ return BAD_VALUE;
+ }
+ }
+ if (buf < 0 || buf >= maxBufferCount) {
+ ALOGE("queueBuffer: slot index out of range [0, %d]: %d",
+ maxBufferCount, buf);
+ return -EINVAL;
+ } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) {
+ ALOGE("queueBuffer: slot %d is not owned by the client "
+ "(state=%d)", buf, mSlots[buf].mBufferState);
+ return -EINVAL;
+ } else if (!mSlots[buf].mRequestBufferCalled) {
+ ALOGE("queueBuffer: slot %d was enqueued without requesting a "
+ "buffer", buf);
+ return -EINVAL;
+ }
+
+ ALOGV("queueBuffer: slot=%d/%llu time=%#llx crop=[%d,%d,%d,%d] "
+ "tr=%#x scale=%s",
+ buf, mFrameCounter + 1, timestamp,
+ crop.left, crop.top, crop.right, crop.bottom,
+ transform, scalingModeName(scalingMode));
+
+ const sp<GraphicBuffer>& graphicBuffer(mSlots[buf].mGraphicBuffer);
+ Rect bufferRect(graphicBuffer->getWidth(), graphicBuffer->getHeight());
+ Rect croppedCrop;
+ crop.intersect(bufferRect, &croppedCrop);
+ if (croppedCrop != crop) {
+ ALOGE("queueBuffer: crop rect is not contained within the "
+ "buffer in slot %d", buf);
+ return -EINVAL;
+ }
+
+ mSlots[buf].mFence = fence;
+ mSlots[buf].mBufferState = BufferSlot::QUEUED;
+ mFrameCounter++;
+ mSlots[buf].mFrameNumber = mFrameCounter;
+
+ BufferItem item;
+ item.mAcquireCalled = mSlots[buf].mAcquireCalled;
+ item.mGraphicBuffer = mSlots[buf].mGraphicBuffer;
+ item.mCrop = crop;
+ item.mTransform = transform & ~NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
+ item.mTransformToDisplayInverse = bool(transform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY);
+ item.mScalingMode = scalingMode;
+ item.mTimestamp = timestamp;
+ item.mIsAutoTimestamp = isAutoTimestamp;
+ item.mFrameNumber = mFrameCounter;
+ item.mBuf = buf;
+ item.mFence = fence;
+ item.mIsDroppable = mDequeueBufferCannotBlock || async;
+
+ if (mQueue.empty()) {
+ // when the queue is empty, we can ignore "mDequeueBufferCannotBlock", and
+ // simply queue this buffer.
+ mQueue.push_back(item);
+ } else {
+ // when the queue is not empty, we need to look at the front buffer
+ // state and see if we need to replace it.
+ Fifo::iterator front(mQueue.begin());
+ if (front->mIsDroppable || !mSynchronousMode) {
+ // buffer slot currently queued is marked free if still tracked
+ if (stillTracking(front)) {
+ mSlots[front->mBuf].mBufferState = BufferSlot::FREE;
+ // reset the frame number of the freed buffer so that it is the first in
+ // line to be dequeued again.
+ mSlots[front->mBuf].mFrameNumber = 0;
+ }
+ // and we record the new buffer in the queued list
+ *front = item;
+ } else {
+ mQueue.push_back(item);
+ }
+ }
+ // always signals that an additional frame should be consumed
+ // to handle max acquired buffer count reached case.
+ listener = mConsumerListener;
+
+ mBufferHasBeenQueued = true;
+ mDequeueCondition.broadcast();
+
+ output->inflate(mDefaultWidth, mDefaultHeight, mTransformHint,
+ mQueue.size());
+
+ } // scope for the lock
+
+ // call back without lock held
+ if (listener != 0) {
+ listener->onFrameAvailable();
+ }
+ return NO_ERROR;
+}
+
+void GonkBufferQueue::cancelBuffer(int buf, const sp<Fence>& fence) {
+ ATRACE_CALL();
+ ALOGV("cancelBuffer: slot=%d", buf);
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ ALOGW("cancelBuffer: GonkBufferQueue has been abandoned!");
+ return;
+ }
+
+ if (buf < 0 || buf >= NUM_BUFFER_SLOTS) {
+ ALOGE("cancelBuffer: slot index out of range [0, %d]: %d",
+ NUM_BUFFER_SLOTS, buf);
+ return;
+ } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) {
+ ALOGE("cancelBuffer: slot %d is not owned by the client (state=%d)",
+ buf, mSlots[buf].mBufferState);
+ return;
+ } else if (fence == NULL) {
+ ALOGE("cancelBuffer: fence is NULL");
+ return;
+ }
+ mSlots[buf].mBufferState = BufferSlot::FREE;
+ mSlots[buf].mFrameNumber = 0;
+ mSlots[buf].mFence = fence;
+ mDequeueCondition.broadcast();
+}
+
+
+status_t GonkBufferQueue::connect(const sp<IBinder>& token,
+ int api, bool producerControlledByApp, QueueBufferOutput* output) {
+ ATRACE_CALL();
+ ALOGV("connect: api=%d producerControlledByApp=%s", api,
+ producerControlledByApp ? "true" : "false");
+ Mutex::Autolock lock(mMutex);
+
+retry:
+ if (mAbandoned) {
+ ALOGE("connect: GonkBufferQueue has been abandoned!");
+ return NO_INIT;
+ }
+
+ if (mConsumerListener == NULL) {
+ ALOGE("connect: GonkBufferQueue has no consumer!");
+ return NO_INIT;
+ }
+
+ if (mConnectedApi != NO_CONNECTED_API) {
+ ALOGE("connect: already connected (cur=%d, req=%d)",
+ mConnectedApi, api);
+ return -EINVAL;
+ }
+
+ // If we disconnect and reconnect quickly, we can be in a state where our slots are
+ // empty but we have many buffers in the queue. This can cause us to run out of
+ // memory if we outrun the consumer. Wait here if it looks like we have too many
+ // buffers queued up.
+ int maxBufferCount = getMaxBufferCountLocked(false); // worst-case, i.e. largest value
+ if (mQueue.size() > (size_t) maxBufferCount) {
+ // TODO: make this bound tighter?
+ ALOGV("queue size is %d, waiting", mQueue.size());
+ mDequeueCondition.wait(mMutex);
+ goto retry;
+ }
+
+ 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:
+ mConnectedApi = api;
+ output->inflate(mDefaultWidth, mDefaultHeight, mTransformHint, mQueue.size());
+
+ // set-up a death notification so that we can disconnect
+ // automatically when/if the remote producer dies.
+ if (token != NULL && token->remoteBinder() != NULL) {
+ status_t err = token->linkToDeath(static_cast<IBinder::DeathRecipient*>(this));
+ if (err == NO_ERROR) {
+ mConnectedProducerToken = token;
+ } else {
+ ALOGE("linkToDeath failed: %s (%d)", strerror(-err), err);
+ }
+ }
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ mBufferHasBeenQueued = false;
+ mDequeueBufferCannotBlock = mConsumerControlledByApp && producerControlledByApp;
+
+ return err;
+}
+
+void GonkBufferQueue::binderDied(const wp<IBinder>& who) {
+ // If we're here, it means that a producer we were connected to died.
+ // We're GUARANTEED that we still are connected to it because it has no other way
+ // to get disconnected -- or -- we wouldn't be here because we're removing this
+ // callback upon disconnect. Therefore, it's okay to read mConnectedApi without
+ // synchronization here.
+ int api = mConnectedApi;
+ this->disconnect(api);
+}
+
+status_t GonkBufferQueue::disconnect(int api) {
+ ATRACE_CALL();
+ ALOGV("disconnect: api=%d", api);
+
+ int err = NO_ERROR;
+ sp<IConsumerListener> listener;
+
+ { // Scope for the lock
+ 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;
+ }
+
+ 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) {
+ freeAllBuffersLocked();
+ mConnectedApi = NO_CONNECTED_API;
+ mDequeueCondition.broadcast();
+ listener = mConsumerListener;
+ } else {
+ ALOGE("disconnect: connected to another api (cur=%d, req=%d)",
+ mConnectedApi, api);
+ err = -EINVAL;
+ }
+ break;
+ default:
+ ALOGE("disconnect: unknown API %d", api);
+ err = -EINVAL;
+ break;
+ }
+ }
+
+ if (listener != NULL) {
+ listener->onBuffersReleased();
+ }
+
+ return err;
+}
+
+void GonkBufferQueue::dumpToString(String8& result, const char* prefix) const {
+ Mutex::Autolock _l(mMutex);
+
+ String8 fifo;
+ int fifoSize = 0;
+ Fifo::const_iterator i(mQueue.begin());
+ while (i != mQueue.end()) {
+ fifo.appendFormat("%02d:%p crop=[%d,%d,%d,%d], "
+ "xform=0x%02x, time=%#llx, scale=%s\n",
+ i->mBuf, i->mGraphicBuffer.get(),
+ i->mCrop.left, i->mCrop.top, i->mCrop.right,
+ i->mCrop.bottom, i->mTransform, i->mTimestamp,
+ scalingModeName(i->mScalingMode)
+ );
+ i++;
+ fifoSize++;
+ }
+
+
+ result.appendFormat(
+ "%s-BufferQueue mMaxAcquiredBufferCount=%d, mDequeueBufferCannotBlock=%d, default-size=[%dx%d], "
+ "default-format=%d, transform-hint=%02x, FIFO(%d)={%s}\n",
+ prefix, mMaxAcquiredBufferCount, mDequeueBufferCannotBlock, mDefaultWidth,
+ mDefaultHeight, mDefaultBufferFormat, mTransformHint,
+ fifoSize, fifo.string());
+
+ struct {
+ const char * operator()(int state) const {
+ switch (state) {
+ case BufferSlot::DEQUEUED: return "DEQUEUED";
+ case BufferSlot::QUEUED: return "QUEUED";
+ case BufferSlot::FREE: return "FREE";
+ case BufferSlot::ACQUIRED: return "ACQUIRED";
+ default: return "Unknown";
+ }
+ }
+ } stateName;
+
+ // just trim the free buffers to not spam the dump
+ int maxBufferCount = 0;
+ for (int i=NUM_BUFFER_SLOTS-1 ; i>=0 ; i--) {
+ const BufferSlot& slot(mSlots[i]);
+ if ((slot.mBufferState != BufferSlot::FREE) || (slot.mGraphicBuffer != NULL)) {
+ maxBufferCount = i+1;
+ break;
+ }
+ }
+
+ for (int i=0 ; i<maxBufferCount ; i++) {
+ const BufferSlot& slot(mSlots[i]);
+ const sp<GraphicBuffer>& buf(slot.mGraphicBuffer);
+ result.appendFormat(
+ "%s%s[%02d:%p] state=%-8s",
+ prefix, (slot.mBufferState == BufferSlot::ACQUIRED)?">":" ", i, buf.get(),
+ stateName(slot.mBufferState)
+ );
+
+ if (buf != NULL) {
+ result.appendFormat(
+ ", %p [%4ux%4u:%4u,%3X]",
+ buf->handle, buf->width, buf->height, buf->stride,
+ buf->format);
+ }
+ result.append("\n");
+ }
+}
+
+void GonkBufferQueue::freeAllBuffersLocked()
+{
+ ALOGW_IF(!mQueue.isEmpty(),
+ "freeAllBuffersLocked called but mQueue is not empty");
+ mQueue.clear();
+ mBufferHasBeenQueued = false;
+ for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
+ mSlots[i].mGraphicBuffer = 0;
+ if (mSlots[i].mTextureClient) {
+ mSlots[i].mTextureClient->ClearRecycleCallback();
+ // release TextureClient in ImageBridge thread
+ RefPtr<TextureClientReleaseTask> task =
+ MakeAndAddRef<TextureClientReleaseTask>(mSlots[i].mTextureClient);
+ mSlots[i].mTextureClient = NULL;
+ ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(task.forget());
+ }
+ if (mSlots[i].mBufferState == BufferSlot::ACQUIRED) {
+ mSlots[i].mNeedsCleanupOnRelease = true;
+ }
+ mSlots[i].mBufferState = BufferSlot::FREE;
+ mSlots[i].mFrameNumber = 0;
+ mSlots[i].mAcquireCalled = false;
+ // destroy fence as GonkBufferQueue now takes ownership
+ mSlots[i].mFence = Fence::NO_FENCE;
+ }
+}
+
+status_t GonkBufferQueue::acquireBuffer(BufferItem *buffer, nsecs_t expectedPresent) {
+ ATRACE_CALL();
+ Mutex::Autolock _l(mMutex);
+
+ // Check that the consumer doesn't currently have the maximum number of
+ // buffers acquired. We allow the max buffer count to be exceeded by one
+ // buffer, so that the consumer can successfully set up the newly acquired
+ // buffer before releasing the old one.
+ int numAcquiredBuffers = 0;
+ for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
+ if (mSlots[i].mBufferState == BufferSlot::ACQUIRED) {
+ numAcquiredBuffers++;
+ }
+ }
+ if (numAcquiredBuffers >= mMaxAcquiredBufferCount+1) {
+ ALOGE("acquireBuffer: max acquired buffer count reached: %d (max=%d)",
+ numAcquiredBuffers, mMaxAcquiredBufferCount);
+ return INVALID_OPERATION;
+ }
+
+ // check if queue is empty
+ // In asynchronous mode the list is guaranteed to be one buffer
+ // deep, while in synchronous mode we use the oldest buffer.
+ if (mQueue.empty()) {
+ return NO_BUFFER_AVAILABLE;
+ }
+
+ Fifo::iterator front(mQueue.begin());
+
+ // If expectedPresent is specified, we may not want to return a buffer yet.
+ // If it's specified and there's more than one buffer queued, we may
+ // want to drop a buffer.
+ if (expectedPresent != 0) {
+ const int MAX_REASONABLE_NSEC = 1000000000ULL; // 1 second
+
+ // The "expectedPresent" argument indicates when the buffer is expected
+ // to be presented on-screen. If the buffer's desired-present time
+ // is earlier (less) than expectedPresent, meaning it'll be displayed
+ // on time or possibly late if we show it ASAP, we acquire and return
+ // it. If we don't want to display it until after the expectedPresent
+ // time, we return PRESENT_LATER without acquiring it.
+ //
+ // To be safe, we don't defer acquisition if expectedPresent is
+ // more than one second in the future beyond the desired present time
+ // (i.e. we'd be holding the buffer for a long time).
+ //
+ // NOTE: code assumes monotonic time values from the system clock are
+ // positive.
+
+ // Start by checking to see if we can drop frames. We skip this check
+ // if the timestamps are being auto-generated by Surface -- if the
+ // app isn't generating timestamps explicitly, they probably don't
+ // want frames to be discarded based on them.
+ while (mQueue.size() > 1 && !mQueue[0].mIsAutoTimestamp) {
+ // If entry[1] is timely, drop entry[0] (and repeat). We apply
+ // an additional criteria here: we only drop the earlier buffer if
+ // our desiredPresent falls within +/- 1 second of the expected
+ // present. Otherwise, bogus desiredPresent times (e.g. 0 or
+ // a small relative timestamp), which normally mean "ignore the
+ // timestamp and acquire immediately", would cause us to drop
+ // frames.
+ //
+ // We may want to add an additional criteria: don't drop the
+ // earlier buffer if entry[1]'s fence hasn't signaled yet.
+ //
+ // (Vector front is [0], back is [size()-1])
+ const BufferItem& bi(mQueue[1]);
+ nsecs_t desiredPresent = bi.mTimestamp;
+ if (desiredPresent < expectedPresent - MAX_REASONABLE_NSEC ||
+ desiredPresent > expectedPresent) {
+ // This buffer is set to display in the near future, or
+ // desiredPresent is garbage. Either way we don't want to
+ // drop the previous buffer just to get this on screen sooner.
+ ALOGV("pts nodrop: des=%lld expect=%lld (%lld) now=%lld",
+ desiredPresent, expectedPresent, desiredPresent - expectedPresent,
+ systemTime(CLOCK_MONOTONIC));
+ break;
+ }
+ ALOGV("pts drop: queue1des=%lld expect=%lld size=%d",
+ desiredPresent, expectedPresent, mQueue.size());
+ if (stillTracking(front)) {
+ // front buffer is still in mSlots, so mark the slot as free
+ mSlots[front->mBuf].mBufferState = BufferSlot::FREE;
+ }
+ mQueue.erase(front);
+ front = mQueue.begin();
+ }
+
+ // See if the front buffer is due.
+ nsecs_t desiredPresent = front->mTimestamp;
+ if (desiredPresent > expectedPresent &&
+ desiredPresent < expectedPresent + MAX_REASONABLE_NSEC) {
+ ALOGV("pts defer: des=%lld expect=%lld (%lld) now=%lld",
+ desiredPresent, expectedPresent, desiredPresent - expectedPresent,
+ systemTime(CLOCK_MONOTONIC));
+ return PRESENT_LATER;
+ }
+
+ ALOGV("pts accept: des=%lld expect=%lld (%lld) now=%lld",
+ desiredPresent, expectedPresent, desiredPresent - expectedPresent,
+ systemTime(CLOCK_MONOTONIC));
+ }
+
+ int buf = front->mBuf;
+ buffer->mGraphicBuffer = mSlots[buf].mGraphicBuffer;
+ buffer->mFrameNumber = mSlots[buf].mFrameNumber;
+ buffer->mBuf = buf;
+ buffer->mFence = mSlots[buf].mFence;
+ ATRACE_BUFFER_INDEX(buf);
+
+ ALOGV("acquireBuffer: acquiring { slot=%d/%llu, buffer=%p }",
+ front->mBuf, front->mFrameNumber,
+ front->mGraphicBuffer->handle);
+ // if front buffer still being tracked update slot state
+ if (stillTracking(front)) {
+ mSlots[buf].mAcquireCalled = true;
+ mSlots[buf].mNeedsCleanupOnRelease = false;
+ mSlots[buf].mBufferState = BufferSlot::ACQUIRED;
+ mSlots[buf].mFence = Fence::NO_FENCE;
+ }
+
+ // If the buffer has previously been acquired by the consumer, set
+ // mGraphicBuffer to NULL to avoid unnecessarily remapping this
+ // buffer on the consumer side.
+ //if (buffer->mAcquireCalled) {
+ // buffer->mGraphicBuffer = NULL;
+ //}
+
+ mQueue.erase(front);
+ mDequeueCondition.broadcast();
+
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueue::releaseBuffer(int buf, uint64_t frameNumber, const sp<Fence>& fence) {
+ ATRACE_CALL();
+
+ if (buf == INVALID_BUFFER_SLOT || fence == NULL) {
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock _l(mMutex);
+
+ // If the frame number has changed because buffer has been reallocated,
+ // we can ignore this releaseBuffer for the old buffer.
+ //if (frameNumber != mSlots[buf].mFrameNumber) {
+ // return STALE_BUFFER_SLOT;
+ //}
+
+
+ // Internal state consistency checks:
+ // Make sure this buffers hasn't been queued while we were owning it (acquired)
+ Fifo::iterator front(mQueue.begin());
+ Fifo::const_iterator const end(mQueue.end());
+ while (front != end) {
+ if (front->mBuf == buf) {
+ LOG_ALWAYS_FATAL("[%s] received new buffer(#%lld) on slot #%d that has not yet been "
+ "acquired", mConsumerName.string(), frameNumber, buf);
+ break; // never reached
+ }
+ front++;
+ }
+
+ // The buffer can now only be released if its in the acquired state
+ if (mSlots[buf].mBufferState == BufferSlot::ACQUIRED) {
+ mSlots[buf].mFence = fence;
+ mSlots[buf].mBufferState = BufferSlot::FREE;
+ } else if (mSlots[buf].mNeedsCleanupOnRelease) {
+ ALOGV("releasing a stale buf %d its state was %d", buf, mSlots[buf].mBufferState);
+ mSlots[buf].mNeedsCleanupOnRelease = false;
+ return STALE_BUFFER_SLOT;
+ } else {
+ ALOGE("attempted to release buf %d but its state was %d", buf, mSlots[buf].mBufferState);
+ return -EINVAL;
+ }
+
+ mDequeueCondition.broadcast();
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueue::consumerConnect(const sp<IConsumerListener>& consumerListener,
+ bool controlledByApp) {
+ ALOGV("consumerConnect controlledByApp=%s",
+ controlledByApp ? "true" : "false");
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ ALOGE("consumerConnect: GonkBufferQueue has been abandoned!");
+ return NO_INIT;
+ }
+ if (consumerListener == NULL) {
+ ALOGE("consumerConnect: consumerListener may not be NULL");
+ return BAD_VALUE;
+ }
+
+ mConsumerListener = consumerListener;
+ mConsumerControlledByApp = controlledByApp;
+
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueue::consumerDisconnect() {
+ ALOGV("consumerDisconnect");
+ Mutex::Autolock lock(mMutex);
+
+ if (mConsumerListener == NULL) {
+ ALOGE("consumerDisconnect: No consumer is connected!");
+ return -EINVAL;
+ }
+
+ mAbandoned = true;
+ mConsumerListener = NULL;
+ mQueue.clear();
+ freeAllBuffersLocked();
+ mDequeueCondition.broadcast();
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueue::getReleasedBuffers(uint32_t* slotMask) {
+ ALOGV("getReleasedBuffers");
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ ALOGE("getReleasedBuffers: GonkBufferQueue has been abandoned!");
+ return NO_INIT;
+ }
+
+ uint32_t mask = 0;
+ for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
+ if (!mSlots[i].mAcquireCalled) {
+ mask |= 1 << i;
+ }
+ }
+
+ // Remove buffers in flight (on the queue) from the mask where acquire has
+ // been called, as the consumer will not receive the buffer address, so
+ // it should not free these slots.
+ Fifo::iterator front(mQueue.begin());
+ while (front != mQueue.end()) {
+ if (front->mAcquireCalled)
+ mask &= ~(1 << front->mBuf);
+ front++;
+ }
+
+ *slotMask = mask;
+
+ ALOGV("getReleasedBuffers: returning mask %#x", mask);
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueue::setDefaultBufferSize(uint32_t w, uint32_t h) {
+ ALOGV("setDefaultBufferSize: w=%d, h=%d", w, h);
+ if (!w || !h) {
+ ALOGE("setDefaultBufferSize: dimensions cannot be 0 (w=%d, h=%d)",
+ w, h);
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mMutex);
+ mDefaultWidth = w;
+ mDefaultHeight = h;
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueue::setDefaultMaxBufferCount(int bufferCount) {
+ ATRACE_CALL();
+ Mutex::Autolock lock(mMutex);
+ return setDefaultMaxBufferCountLocked(bufferCount);
+}
+
+status_t GonkBufferQueue::disableAsyncBuffer() {
+ ATRACE_CALL();
+ Mutex::Autolock lock(mMutex);
+ if (mConsumerListener != NULL) {
+ ALOGE("disableAsyncBuffer: consumer already connected!");
+ return INVALID_OPERATION;
+ }
+ mUseAsyncBuffer = false;
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueue::setMaxAcquiredBufferCount(int maxAcquiredBuffers) {
+ ATRACE_CALL();
+ Mutex::Autolock lock(mMutex);
+ if (maxAcquiredBuffers < 1 || maxAcquiredBuffers > MAX_MAX_ACQUIRED_BUFFERS) {
+ ALOGE("setMaxAcquiredBufferCount: invalid count specified: %d",
+ maxAcquiredBuffers);
+ return BAD_VALUE;
+ }
+ if (mConnectedApi != NO_CONNECTED_API) {
+ return INVALID_OPERATION;
+ }
+ mMaxAcquiredBufferCount = maxAcquiredBuffers;
+ return NO_ERROR;
+}
+
+int GonkBufferQueue::getMinUndequeuedBufferCount(bool async) const {
+ // if dequeueBuffer is allowed to error out, we don't have to
+ // add an extra buffer.
+ if (!mUseAsyncBuffer)
+ return mMaxAcquiredBufferCount;
+
+ // we're in async mode, or we want to prevent the app to
+ // deadlock itself, we throw-in an extra buffer to guarantee it.
+ if (mDequeueBufferCannotBlock || async || !mSynchronousMode)
+ return mMaxAcquiredBufferCount + 1;
+
+ return mMaxAcquiredBufferCount;
+}
+
+int GonkBufferQueue::getMinMaxBufferCountLocked(bool async) const {
+ return getMinUndequeuedBufferCount(async) + 1;
+}
+
+int GonkBufferQueue::getMaxBufferCountLocked(bool async) const {
+ int minMaxBufferCount = getMinMaxBufferCountLocked(async);
+
+ int maxBufferCount = mDefaultMaxBufferCount;
+ if (maxBufferCount < minMaxBufferCount) {
+ maxBufferCount = minMaxBufferCount;
+ }
+ if (mOverrideMaxBufferCount != 0) {
+ assert(mOverrideMaxBufferCount >= minMaxBufferCount);
+ maxBufferCount = mOverrideMaxBufferCount;
+ }
+
+ // Any buffers that are dequeued by the producer or sitting in the queue
+ // waiting to be consumed need to have their slots preserved. Such
+ // buffers will temporarily keep the max buffer count up until the slots
+ // no longer need to be preserved.
+ for (int i = maxBufferCount; i < NUM_BUFFER_SLOTS; i++) {
+ BufferSlot::BufferState state = mSlots[i].mBufferState;
+ if (state == BufferSlot::QUEUED || state == BufferSlot::DEQUEUED) {
+ maxBufferCount = i + 1;
+ }
+ }
+
+ return maxBufferCount;
+}
+
+bool GonkBufferQueue::stillTracking(const BufferItem *item) const {
+ const BufferSlot &slot = mSlots[item->mBuf];
+
+ ALOGV("stillTracking?: item: { slot=%d/%llu, buffer=%p }, "
+ "slot: { slot=%d/%llu, buffer=%p }",
+ item->mBuf, item->mFrameNumber,
+ (item->mGraphicBuffer.get() ? item->mGraphicBuffer->handle : 0),
+ item->mBuf, slot.mFrameNumber,
+ (slot.mGraphicBuffer.get() ? slot.mGraphicBuffer->handle : 0));
+
+ // Compare item with its original buffer slot. We can check the slot
+ // as the buffer would not be moved to a different slot by the producer.
+ return (slot.mGraphicBuffer != NULL &&
+ item->mGraphicBuffer->handle == slot.mGraphicBuffer->handle);
+}
+
+GonkBufferQueue::ProxyConsumerListener::ProxyConsumerListener(
+ const wp<ConsumerListener>& consumerListener):
+ mConsumerListener(consumerListener) {}
+
+GonkBufferQueue::ProxyConsumerListener::~ProxyConsumerListener() {}
+
+void GonkBufferQueue::ProxyConsumerListener::onFrameAvailable() {
+ sp<ConsumerListener> listener(mConsumerListener.promote());
+ if (listener != NULL) {
+ listener->onFrameAvailable();
+ }
+}
+
+void GonkBufferQueue::ProxyConsumerListener::onBuffersReleased() {
+ sp<ConsumerListener> listener(mConsumerListener.promote());
+ if (listener != NULL) {
+ listener->onBuffersReleased();
+ }
+}
+
+}; // namespace android
diff --git a/widget/gonk/nativewindow/GonkBufferQueueKK.h b/widget/gonk/nativewindow/GonkBufferQueueKK.h
new file mode 100644
index 000000000..01905427d
--- /dev/null
+++ b/widget/gonk/nativewindow/GonkBufferQueueKK.h
@@ -0,0 +1,583 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2013 Mozilla Foundation
+ *
+ * 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 NATIVEWINDOW_GONKBUFFERQUEUE_KK_H
+#define NATIVEWINDOW_GONKBUFFERQUEUE_KK_H
+
+#include <gui/IConsumerListener.h>
+#include <gui/IGraphicBufferAlloc.h>
+#include <gui/IGraphicBufferProducer.h>
+#include "IGonkGraphicBufferConsumer.h"
+
+#include <ui/Fence.h>
+#include <ui/GraphicBuffer.h>
+
+#include <utils/String8.h>
+#include <utils/Vector.h>
+#include <utils/threads.h>
+
+#include "mozilla/layers/LayersSurfaces.h"
+#include "mozilla/layers/TextureClient.h"
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+class GonkBufferQueue : public BnGraphicBufferProducer,
+ public BnGonkGraphicBufferConsumer,
+ private IBinder::DeathRecipient
+{
+ typedef mozilla::layers::TextureClient TextureClient;
+
+public:
+ enum { MIN_UNDEQUEUED_BUFFERS = 2 };
+ enum { NUM_BUFFER_SLOTS = 32 };
+ enum { NO_CONNECTED_API = 0 };
+ enum { INVALID_BUFFER_SLOT = -1 };
+ enum { STALE_BUFFER_SLOT = 1, NO_BUFFER_AVAILABLE, PRESENT_LATER };
+
+ // When in async mode we reserve two slots in order to guarantee that the
+ // producer and consumer can run asynchronously.
+ enum { MAX_MAX_ACQUIRED_BUFFERS = NUM_BUFFER_SLOTS - 2 };
+
+ // for backward source compatibility
+ typedef ::android::ConsumerListener ConsumerListener;
+
+ // ProxyConsumerListener is a ConsumerListener implementation that keeps a weak
+ // reference to the actual consumer object. It forwards all calls to that
+ // consumer object so long as it exists.
+ //
+ // This class exists to avoid having a circular reference between the
+ // GonkBufferQueue object and the consumer object. The reason this can't be a weak
+ // reference in the GonkBufferQueue class is because we're planning to expose the
+ // consumer side of a GonkBufferQueue as a binder interface, which doesn't support
+ // weak references.
+ class ProxyConsumerListener : public BnConsumerListener {
+ public:
+ ProxyConsumerListener(const wp<ConsumerListener>& consumerListener);
+ virtual ~ProxyConsumerListener();
+ virtual void onFrameAvailable();
+ virtual void onBuffersReleased();
+ private:
+ // mConsumerListener is a weak reference to the IConsumerListener. This is
+ // the raison d'etre of ProxyConsumerListener.
+ wp<ConsumerListener> mConsumerListener;
+ };
+
+
+ // BufferQueue manages a pool of gralloc memory slots to be used by
+ // producers and consumers. allocator is used to allocate all the
+ // needed gralloc buffers.
+ GonkBufferQueue(bool allowSynchronousMode = true,
+ const sp<IGraphicBufferAlloc>& allocator = NULL);
+ virtual ~GonkBufferQueue();
+
+ /*
+ * IBinder::DeathRecipient interface
+ */
+
+ virtual void binderDied(const wp<IBinder>& who);
+
+ /*
+ * IGraphicBufferProducer interface
+ */
+
+ // Query native window attributes. The "what" values are enumerated in
+ // window.h (e.g. NATIVE_WINDOW_FORMAT).
+ virtual int query(int what, int* value);
+
+ // setBufferCount updates the number of available buffer slots. If this
+ // method succeeds, buffer slots will be both unallocated and owned by
+ // the GonkBufferQueue object (i.e. they are not owned by the producer or
+ // consumer).
+ //
+ // This will fail if the producer has dequeued any buffers, or if
+ // bufferCount is invalid. bufferCount must generally be a value
+ // between the minimum undequeued buffer count and NUM_BUFFER_SLOTS
+ // (inclusive). It may also be set to zero (the default) to indicate
+ // that the producer does not wish to set a value. The minimum value
+ // can be obtained by calling query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+ // ...).
+ //
+ // This may only be called by the producer. The consumer will be told
+ // to discard buffers through the onBuffersReleased callback.
+ virtual status_t setBufferCount(int bufferCount);
+
+ // requestBuffer returns the GraphicBuffer for slot N.
+ //
+ // In normal operation, this is called the first time slot N is returned
+ // by dequeueBuffer. It must be called again if dequeueBuffer returns
+ // flags indicating that previously-returned buffers are no longer valid.
+ virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf);
+
+ // dequeueBuffer gets the next buffer slot index for the producer 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 fence parameter will be updated to hold the fence associated with
+ // the buffer. The contents of the buffer must not be overwritten until the
+ // fence signals. If the fence is Fence::NO_FENCE, the buffer may be
+ // written immediately.
+ //
+ // 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. If width and height are both zero, the
+ // default values specified by setDefaultBufferSize() are used instead.
+ //
+ // The pixel formats are enumerated in graphics.h, e.g.
+ // HAL_PIXEL_FORMAT_RGBA_8888. If the format is 0, the default format
+ // will be used.
+ //
+ // The usage argument specifies gralloc buffer usage flags. The values
+ // are enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER. These
+ // will be merged with the usage flags specified by setConsumerUsageBits.
+ //
+ // The return value may be a negative error value or a non-negative
+ // collection of flags. If the flags are set, the return values are
+ // valid, but additional actions must be performed.
+ //
+ // If IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION is set, the
+ // producer must discard cached GraphicBuffer references for the slot
+ // returned in buf.
+ // If IGraphicBufferProducer::RELEASE_ALL_BUFFERS is set, the producer
+ // must discard cached GraphicBuffer references for all slots.
+ //
+ // In both cases, the producer will need to call requestBuffer to get a
+ // GraphicBuffer handle for the returned slot.
+ virtual status_t dequeueBuffer(int *buf, sp<Fence>* fence, bool async,
+ uint32_t width, uint32_t height, uint32_t format, uint32_t usage);
+
+ // queueBuffer returns a filled buffer to the GonkBufferQueue.
+ //
+ // Additional data is provided in the QueueBufferInput struct. Notably,
+ // 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 producer-specific and should be documented by the
+ // producer.
+ //
+ // The caller may provide a fence that signals when all rendering
+ // operations have completed. Alternatively, NO_FENCE may be used,
+ // indicating that the buffer is ready immediately.
+ //
+ // Some values are returned in the output struct: the current settings
+ // for default width and height, the current transform hint, and the
+ // number of queued buffers.
+ virtual status_t queueBuffer(int buf,
+ const QueueBufferInput& input, QueueBufferOutput* output);
+
+ // cancelBuffer returns a dequeued buffer to the GonkBufferQueue, but doesn't
+ // queue it for use by the consumer.
+ //
+ // The buffer will not be overwritten until the fence signals. The fence
+ // will usually be the one obtained from dequeueBuffer.
+ virtual void cancelBuffer(int buf, const sp<Fence>& fence);
+
+ // setSynchronousMode sets 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 acquired in order. In asynchronous mode,
+ // a queued buffer may be replaced by a subsequently queued buffer.
+ //
+ // The default mode is synchronous.
+ // This should be called only during initialization.
+ virtual status_t setSynchronousMode(bool enabled);
+
+ // connect attempts to connect a producer API to the GonkBufferQueue. This
+ // must be called before any other IGraphicBufferProducer methods are
+ // called except for getAllocator. A consumer must already be connected.
+ //
+ // This method will fail if connect was previously called on the
+ // GonkBufferQueue and no corresponding disconnect call was made (i.e. if
+ // it's still connected to a producer).
+ //
+ // APIs are enumerated in window.h (e.g. NATIVE_WINDOW_API_CPU).
+ virtual status_t connect(const sp<IBinder>& token,
+ int api, bool producerControlledByApp, QueueBufferOutput* output);
+
+ // disconnect attempts to disconnect a producer API from the GonkBufferQueue.
+ // Calling this method will cause any subsequent calls to other
+ // IGraphicBufferProducer 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 GonkBufferQueue is not currently
+ // connected to the specified producer API.
+ virtual status_t disconnect(int api);
+
+ /*
+ * IGraphicBufferConsumer interface
+ */
+
+ // acquireBuffer attempts to acquire ownership of the next pending buffer in
+ // the GonkBufferQueue. If no buffer is pending then it returns -EINVAL. If a
+ // buffer is successfully acquired, the information about the buffer is
+ // returned in BufferItem. If the buffer returned had previously been
+ // acquired then the BufferItem::mGraphicBuffer field of buffer is set to
+ // NULL and it is assumed that the consumer still holds a reference to the
+ // buffer.
+ //
+ // If presentWhen is nonzero, it indicates the time when the buffer will
+ // be displayed on screen. If the buffer's timestamp is farther in the
+ // future, the buffer won't be acquired, and PRESENT_LATER will be
+ // returned. The presentation time is in nanoseconds, and the time base
+ // is CLOCK_MONOTONIC.
+ virtual status_t acquireBuffer(BufferItem *buffer, nsecs_t presentWhen);
+
+ // releaseBuffer releases a buffer slot from the consumer back to the
+ // GonkBufferQueue. This may be done while the buffer's contents are still
+ // being accessed. The fence will signal when the buffer is no longer
+ // in use. frameNumber is used to indentify the exact buffer returned.
+ //
+ // If releaseBuffer returns STALE_BUFFER_SLOT, then the consumer must free
+ // any references to the just-released buffer that it might have, as if it
+ // had received a onBuffersReleased() call with a mask set for the released
+ // buffer.
+ //
+ // Note that the dependencies on EGL will be removed once we switch to using
+ // the Android HW Sync HAL.
+ virtual status_t releaseBuffer(int buf, uint64_t frameNumber,
+ const sp<Fence>& releaseFence);
+
+ // consumerConnect connects a consumer to the GonkBufferQueue. Only one
+ // consumer may be connected, and when that consumer disconnects the
+ // GonkBufferQueue is placed into the "abandoned" state, causing most
+ // interactions with the GonkBufferQueue by the producer to fail.
+ // controlledByApp indicates whether the consumer is controlled by
+ // the application.
+ //
+ // consumer may not be NULL.
+ virtual status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp);
+
+ // consumerDisconnect disconnects a consumer from the GonkBufferQueue. All
+ // buffers will be freed and the GonkBufferQueue is placed in the "abandoned"
+ // state, causing most interactions with the GonkBufferQueue by the producer to
+ // fail.
+ virtual status_t consumerDisconnect();
+
+ // getReleasedBuffers sets the value pointed to by slotMask to a bit mask
+ // indicating which buffer slots have been released by the GonkBufferQueue
+ // but have not yet been released by the consumer.
+ //
+ // This should be called from the onBuffersReleased() callback.
+ virtual status_t getReleasedBuffers(uint32_t* slotMask);
+
+ // setDefaultBufferSize is used to set the size of buffers returned by
+ // dequeueBuffer when a width and height of zero is requested. Default
+ // is 1x1.
+ virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h);
+
+ // setDefaultMaxBufferCount sets the default value for the maximum buffer
+ // count (the initial default is 2). If the producer has requested a
+ // buffer count using setBufferCount, the default buffer count will only
+ // take effect if the producer sets the count back to zero.
+ //
+ // The count must be between 2 and NUM_BUFFER_SLOTS, inclusive.
+ virtual status_t setDefaultMaxBufferCount(int bufferCount);
+
+ // disableAsyncBuffer disables the extra buffer used in async mode
+ // (when both producer and consumer have set their "isControlledByApp"
+ // flag) and has dequeueBuffer() return WOULD_BLOCK instead.
+ //
+ // This can only be called before consumerConnect().
+ virtual status_t disableAsyncBuffer();
+
+ // setMaxAcquiredBufferCount sets the maximum number of buffers that can
+ // be acquired by the consumer at one time (default 1). This call will
+ // fail if a producer is connected to the GonkBufferQueue.
+ virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers);
+
+ // setConsumerName sets the name used in logging
+ virtual void setConsumerName(const String8& name);
+
+ // setDefaultBufferFormat allows the GonkBufferQueue to create
+ // GraphicBuffers of a defaultFormat if no format is specified
+ // in dequeueBuffer. Formats are enumerated in graphics.h; the
+ // initial default is HAL_PIXEL_FORMAT_RGBA_8888.
+ virtual status_t setDefaultBufferFormat(uint32_t defaultFormat);
+
+ // setConsumerUsageBits will turn on additional usage bits for dequeueBuffer.
+ // These are merged with the bits passed to dequeueBuffer. The values are
+ // enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER; the default is 0.
+ virtual status_t setConsumerUsageBits(uint32_t usage);
+
+ // setTransformHint bakes in rotation to buffers so overlays can be used.
+ // The values are enumerated in window.h, e.g.
+ // NATIVE_WINDOW_TRANSFORM_ROT_90. The default is 0 (no transform).
+ virtual status_t setTransformHint(uint32_t hint);
+
+ // dump our state in a String
+ virtual void dumpToString(String8& result, const char* prefix) const;
+
+ already_AddRefed<TextureClient> getTextureClientFromBuffer(ANativeWindowBuffer* buffer);
+
+ int getSlotFromTextureClientLocked(TextureClient* client) const;
+
+private:
+ // freeBufferLocked frees the GraphicBuffer and sync resources for the
+ // given slot.
+ //void freeBufferLocked(int index);
+
+ // freeAllBuffersLocked frees the GraphicBuffer and sync resources for
+ // all slots.
+ void freeAllBuffersLocked();
+
+ // setDefaultMaxBufferCountLocked sets the maximum number of buffer slots
+ // that will be used if the producer does not override the buffer slot
+ // count. The count must be between 2 and NUM_BUFFER_SLOTS, inclusive.
+ // The initial default is 2.
+ status_t setDefaultMaxBufferCountLocked(int count);
+
+ // getMinUndequeuedBufferCount returns the minimum number of buffers
+ // that must remain in a state other than DEQUEUED.
+ // The async parameter tells whether we're in asynchronous mode.
+ int getMinUndequeuedBufferCount(bool async) const;
+
+ // getMinBufferCountLocked returns the minimum number of buffers allowed
+ // given the current GonkBufferQueue state.
+ // The async parameter tells whether we're in asynchronous mode.
+ int getMinMaxBufferCountLocked(bool async) const;
+
+ // getMaxBufferCountLocked returns the maximum number of buffers that can
+ // be allocated at once. This value depends upon the following member
+ // variables:
+ //
+ // mDequeueBufferCannotBlock
+ // mMaxAcquiredBufferCount
+ // mDefaultMaxBufferCount
+ // mOverrideMaxBufferCount
+ // async parameter
+ //
+ // Any time one of these member variables is changed while a producer is
+ // connected, mDequeueCondition must be broadcast.
+ int getMaxBufferCountLocked(bool async) const;
+
+ // stillTracking returns true iff the buffer item is still being tracked
+ // in one of the slots.
+ bool stillTracking(const BufferItem *item) const;
+
+ struct BufferSlot {
+
+ BufferSlot()
+ : mBufferState(BufferSlot::FREE),
+ mRequestBufferCalled(false),
+ mFrameNumber(0),
+ mAcquireCalled(false),
+ mNeedsCleanupOnRelease(false) {
+ }
+
+ // mGraphicBuffer points to the buffer allocated for this slot or is NULL
+ // if no buffer has been allocated.
+ sp<GraphicBuffer> mGraphicBuffer;
+
+ // mTextureClient is a thin abstraction over remotely allocated GraphicBuffer.
+ RefPtr<TextureClient> mTextureClient;
+
+ // BufferState represents the different states in which a buffer slot
+ // can be. All slots are initially FREE.
+ enum BufferState {
+ // FREE indicates that the buffer is available to be dequeued
+ // by the producer. The buffer may be in use by the consumer for
+ // a finite time, so the buffer must not be modified until the
+ // associated fence is signaled.
+ //
+ // The slot is "owned" by GonkBufferQueue. It transitions to DEQUEUED
+ // when dequeueBuffer is called.
+ FREE = 0,
+
+ // DEQUEUED indicates that the buffer has been dequeued by the
+ // producer, but has not yet been queued or canceled. The
+ // producer may modify the buffer's contents as soon as the
+ // associated ready fence is signaled.
+ //
+ // The slot is "owned" by the producer. It can transition to
+ // QUEUED (via queueBuffer) or back to FREE (via cancelBuffer).
+ DEQUEUED = 1,
+
+ // QUEUED indicates that the buffer has been filled by the
+ // producer and queued for use by the consumer. The buffer
+ // contents may continue to be modified for a finite time, so
+ // the contents must not be accessed until the associated fence
+ // is signaled.
+ //
+ // The slot is "owned" by GonkBufferQueue. It can transition to
+ // ACQUIRED (via acquireBuffer) or to FREE (if another buffer is
+ // queued in asynchronous mode).
+ QUEUED = 2,
+
+ // ACQUIRED indicates that the buffer has been acquired by the
+ // consumer. As with QUEUED, the contents must not be accessed
+ // by the consumer until the fence is signaled.
+ //
+ // The slot is "owned" by the consumer. It transitions to FREE
+ // when releaseBuffer is called.
+ ACQUIRED = 3
+ };
+
+ // mBufferState is the current state of this buffer slot.
+ BufferState mBufferState;
+
+ // mRequestBufferCalled is used for validating that the producer did
+ // call requestBuffer() when told to do so. Technically this is not
+ // needed but useful for debugging and catching producer bugs.
+ bool mRequestBufferCalled;
+
+ // mFrameNumber is the number of the queued frame for this slot. This
+ // is used to dequeue buffers in LRU order (useful because buffers
+ // may be released before their release fence is signaled).
+ uint64_t mFrameNumber;
+
+ // mFence is a fence which will signal when work initiated by the
+ // previous owner of the buffer is finished. When the buffer is FREE,
+ // the fence indicates when the consumer has finished reading
+ // from the buffer, or when the producer has finished writing if it
+ // called cancelBuffer after queueing some writes. When the buffer is
+ // QUEUED, it indicates when the producer has finished filling the
+ // buffer. When the buffer is DEQUEUED or ACQUIRED, the fence has been
+ // passed to the consumer or producer along with ownership of the
+ // buffer, and mFence is set to NO_FENCE.
+ sp<Fence> mFence;
+
+ // Indicates whether this buffer has been seen by a consumer yet
+ bool mAcquireCalled;
+
+ // Indicates whether this buffer needs to be cleaned up by the
+ // consumer. This is set when a buffer in ACQUIRED state is freed.
+ // It causes releaseBuffer to return STALE_BUFFER_SLOT.
+ bool mNeedsCleanupOnRelease;
+ };
+
+ // mSlots is the array of buffer slots that must be mirrored on the
+ // producer side. This allows buffer ownership to be transferred between
+ // the producer and consumer 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 dequeueBuffer() if a width and height of zero is specified.
+ uint32_t mDefaultWidth;
+
+ // mDefaultHeight holds the default height of allocated buffers. It is used
+ // in dequeueBuffer() if a width and height of zero is specified.
+ uint32_t mDefaultHeight;
+
+ // mMaxAcquiredBufferCount is the number of buffers that the consumer may
+ // acquire at one time. It defaults to 1 and can be changed by the
+ // consumer via the setMaxAcquiredBufferCount method, but this may only be
+ // done when no producer is connected to the GonkBufferQueue.
+ //
+ // This value is used to derive the value returned for the
+ // MIN_UNDEQUEUED_BUFFERS query by the producer.
+ int mMaxAcquiredBufferCount;
+
+ // mDefaultMaxBufferCount is the default limit on the number of buffers
+ // that will be allocated at one time. This default limit is set by the
+ // consumer. The limit (as opposed to the default limit) may be
+ // overridden by the producer.
+ int mDefaultMaxBufferCount;
+
+ // mOverrideMaxBufferCount is the limit on the number of buffers that will
+ // be allocated at one time. This value is set by the image producer by
+ // calling setBufferCount. The default is zero, which means the producer
+ // doesn't care about the number of buffers in the pool. In that case
+ // mDefaultMaxBufferCount is used as the limit.
+ int mOverrideMaxBufferCount;
+
+ // mGraphicBufferAlloc is the connection to SurfaceFlinger that is used to
+ // allocate new GraphicBuffer objects.
+ sp<IGraphicBufferAlloc> mGraphicBufferAlloc;
+
+ // mConsumerListener is used to notify the connected consumer of
+ // asynchronous events that it may wish to react to. It is initially set
+ // to NULL and is written by consumerConnect and consumerDisconnect.
+ sp<IConsumerListener> mConsumerListener;
+
+ // mSynchronousMode whether we're in synchronous mode or not
+ bool mSynchronousMode;
+
+ // mConsumerControlledByApp whether the connected consumer is controlled by the
+ // application.
+ bool mConsumerControlledByApp;
+
+ // mDequeueBufferCannotBlock whether dequeueBuffer() isn't allowed to block.
+ // this flag is set during connect() when both consumer and producer are controlled
+ // by the application.
+ bool mDequeueBufferCannotBlock;
+
+ // mUseAsyncBuffer whether an extra buffer is used in async mode to prevent
+ // dequeueBuffer() from ever blocking.
+ bool mUseAsyncBuffer;
+
+ // mConnectedApi indicates the producer API that is currently connected
+ // to this GonkBufferQueue. 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<BufferItem> Fifo;
+ Fifo mQueue;
+
+ // mAbandoned indicates that the GonkBufferQueue will no longer be used to
+ // consume image buffers pushed to it using the IGraphicBufferProducer
+ // interface. It is initialized to false, and set to true in the
+ // consumerDisconnect method. A GonkBufferQueue that has been abandoned will
+ // return the NO_INIT error from all IGraphicBufferProducer methods
+ // capable of returning an error.
+ bool mAbandoned;
+
+ // mConsumerName is a string used to identify the GonkBufferQueue in log
+ // messages. It is set by the setConsumerName method.
+ String8 mConsumerName;
+
+ // mMutex is the mutex used to prevent concurrent access to the member
+ // variables of GonkBufferQueue objects. It must be locked whenever the
+ // member variables are accessed.
+ mutable Mutex mMutex;
+
+ // mFrameCounter is the free running counter, incremented on every
+ // successful queueBuffer call, and buffer allocation.
+ uint64_t mFrameCounter;
+
+ // mBufferHasBeenQueued is true once a buffer has been queued. It is
+ // reset when something causes all buffers to be freed (e.g. changing the
+ // buffer count).
+ bool mBufferHasBeenQueued;
+
+ // mDefaultBufferFormat can be set so it will override
+ // the buffer format when it isn't specified in dequeueBuffer
+ uint32_t mDefaultBufferFormat;
+
+ // mConsumerUsageBits contains flags the consumer wants for GraphicBuffers
+ uint32_t mConsumerUsageBits;
+
+ // mTransformHint is used to optimize for screen rotations
+ uint32_t mTransformHint;
+
+ // mConnectedProducerToken is used to set a binder death notification on the producer
+ sp<IBinder> mConnectedProducerToken;
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_GUI_BUFFERQUEUE_H
diff --git a/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferItem.cpp b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferItem.cpp
new file mode 100644
index 000000000..7df72bf68
--- /dev/null
+++ b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferItem.cpp
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ * Copyright (C) 2014 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "GonkBufferItem.h"
+
+#include <ui/Fence.h>
+#include <ui/GraphicBuffer.h>
+
+#include <system/window.h>
+
+namespace android {
+
+GonkBufferItem::GonkBufferItem() :
+ mTransform(0),
+ mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+ mTimestamp(0),
+ mIsAutoTimestamp(false),
+ mFrameNumber(0),
+ mSlot(INVALID_BUFFER_SLOT),
+ mIsDroppable(false),
+ mAcquireCalled(false),
+ mTransformToDisplayInverse(false) {
+ mCrop.makeInvalid();
+}
+
+GonkBufferItem::operator IGonkGraphicBufferConsumer::BufferItem() const {
+ IGonkGraphicBufferConsumer::BufferItem bufferItem;
+ bufferItem.mGraphicBuffer = mGraphicBuffer;
+ bufferItem.mFence = mFence;
+ bufferItem.mCrop = mCrop;
+ bufferItem.mTransform = mTransform;
+ bufferItem.mScalingMode = mScalingMode;
+ bufferItem.mTimestamp = mTimestamp;
+ bufferItem.mIsAutoTimestamp = mIsAutoTimestamp;
+ bufferItem.mFrameNumber = mFrameNumber;
+ bufferItem.mBuf = mSlot;
+ bufferItem.mIsDroppable = mIsDroppable;
+ bufferItem.mAcquireCalled = mAcquireCalled;
+ bufferItem.mTransformToDisplayInverse = mTransformToDisplayInverse;
+ return bufferItem;
+}
+
+size_t GonkBufferItem::getPodSize() const {
+ size_t c = sizeof(mCrop) +
+ sizeof(mTransform) +
+ sizeof(mScalingMode) +
+ sizeof(mTimestamp) +
+ sizeof(mIsAutoTimestamp) +
+ sizeof(mFrameNumber) +
+ sizeof(mSlot) +
+ sizeof(mIsDroppable) +
+ sizeof(mAcquireCalled) +
+ sizeof(mTransformToDisplayInverse);
+ return c;
+}
+
+size_t GonkBufferItem::getFlattenedSize() const {
+ size_t c = 0;
+ if (mGraphicBuffer != 0) {
+ c += mGraphicBuffer->getFlattenedSize();
+ FlattenableUtils::align<4>(c);
+ }
+ if (mFence != 0) {
+ c += mFence->getFlattenedSize();
+ FlattenableUtils::align<4>(c);
+ }
+ return sizeof(int32_t) + c + getPodSize();
+}
+
+size_t GonkBufferItem::getFdCount() const {
+ size_t c = 0;
+ if (mGraphicBuffer != 0) {
+ c += mGraphicBuffer->getFdCount();
+ }
+ if (mFence != 0) {
+ c += mFence->getFdCount();
+ }
+ return c;
+}
+
+status_t GonkBufferItem::flatten(
+ void*& buffer, size_t& size, int*& fds, size_t& count) const {
+
+ // make sure we have enough space
+ if (count < GonkBufferItem::getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ // content flags are stored first
+ uint32_t& flags = *static_cast<uint32_t*>(buffer);
+
+ // advance the pointer
+ FlattenableUtils::advance(buffer, size, sizeof(uint32_t));
+
+ flags = 0;
+ if (mGraphicBuffer != 0) {
+ status_t err = mGraphicBuffer->flatten(buffer, size, fds, count);
+ if (err) return err;
+ size -= FlattenableUtils::align<4>(buffer);
+ flags |= 1;
+ }
+ if (mFence != 0) {
+ status_t err = mFence->flatten(buffer, size, fds, count);
+ if (err) return err;
+ size -= FlattenableUtils::align<4>(buffer);
+ flags |= 2;
+ }
+
+ // check we have enough space (in case flattening the fence/graphicbuffer lied to us)
+ if (size < getPodSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(buffer, size, mCrop);
+ FlattenableUtils::write(buffer, size, mTransform);
+ FlattenableUtils::write(buffer, size, mScalingMode);
+ FlattenableUtils::write(buffer, size, mTimestamp);
+ FlattenableUtils::write(buffer, size, mIsAutoTimestamp);
+ FlattenableUtils::write(buffer, size, mFrameNumber);
+ FlattenableUtils::write(buffer, size, mSlot);
+ FlattenableUtils::write(buffer, size, mIsDroppable);
+ FlattenableUtils::write(buffer, size, mAcquireCalled);
+ FlattenableUtils::write(buffer, size, mTransformToDisplayInverse);
+
+ return NO_ERROR;
+}
+
+status_t GonkBufferItem::unflatten(
+ void const*& buffer, size_t& size, int const*& fds, size_t& count) {
+
+ if (size < sizeof(uint32_t))
+ return NO_MEMORY;
+
+ uint32_t flags = 0;
+ FlattenableUtils::read(buffer, size, flags);
+
+ if (flags & 1) {
+ mGraphicBuffer = new GraphicBuffer();
+ status_t err = mGraphicBuffer->unflatten(buffer, size, fds, count);
+ if (err) return err;
+ size -= FlattenableUtils::align<4>(buffer);
+ }
+
+ if (flags & 2) {
+ mFence = new Fence();
+ status_t err = mFence->unflatten(buffer, size, fds, count);
+ if (err) return err;
+ size -= FlattenableUtils::align<4>(buffer);
+ }
+
+ // check we have enough space
+ if (size < getPodSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, mCrop);
+ FlattenableUtils::read(buffer, size, mTransform);
+ FlattenableUtils::read(buffer, size, mScalingMode);
+ FlattenableUtils::read(buffer, size, mTimestamp);
+ FlattenableUtils::read(buffer, size, mIsAutoTimestamp);
+ FlattenableUtils::read(buffer, size, mFrameNumber);
+ FlattenableUtils::read(buffer, size, mSlot);
+ FlattenableUtils::read(buffer, size, mIsDroppable);
+ FlattenableUtils::read(buffer, size, mAcquireCalled);
+ FlattenableUtils::read(buffer, size, mTransformToDisplayInverse);
+
+ return NO_ERROR;
+}
+
+const char* GonkBufferItem::scalingModeName(uint32_t scalingMode) {
+ switch (scalingMode) {
+ case NATIVE_WINDOW_SCALING_MODE_FREEZE: return "FREEZE";
+ case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: return "SCALE_TO_WINDOW";
+ case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP: return "SCALE_CROP";
+ default: return "Unknown";
+ }
+}
+
+} // namespace android
diff --git a/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferItem.h b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferItem.h
new file mode 100644
index 000000000..b2d6d3068
--- /dev/null
+++ b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferItem.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ * Copyright (C) 2014 Mozilla Foundation
+ *
+ * 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 NATIVEWINDOW_GONKBUFFERITEM_LL_H
+#define NATIVEWINDOW_GONKBUFFERITEM_LL_H
+
+#include "IGonkGraphicBufferConsumerLL.h"
+
+#include <ui/Rect.h>
+
+#include <utils/Flattenable.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+class Fence;
+class GraphicBuffer;
+
+class GonkBufferItem : public Flattenable<GonkBufferItem> {
+ friend class Flattenable<GonkBufferItem>;
+ size_t getPodSize() const;
+ size_t getFlattenedSize() const;
+ size_t getFdCount() const;
+ status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
+ status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
+
+ public:
+ // The default value of mBuf, used to indicate this doesn't correspond to a slot.
+ enum { INVALID_BUFFER_SLOT = -1 };
+ GonkBufferItem();
+ operator IGonkGraphicBufferConsumer::BufferItem() const;
+
+ static const char* scalingModeName(uint32_t scalingMode);
+
+ // mGraphicBuffer points to the buffer allocated for this slot, or is NULL
+ // if the buffer in this slot has been acquired in the past (see
+ // BufferSlot.mAcquireCalled).
+ sp<GraphicBuffer> mGraphicBuffer;
+
+ // mFence is a fence that will signal when the buffer is idle.
+ sp<Fence> mFence;
+
+ // mCrop is the current crop rectangle for this buffer slot.
+ Rect mCrop;
+
+ // mTransform is the current transform flags for this buffer slot.
+ // refer to NATIVE_WINDOW_TRANSFORM_* in <window.h>
+ uint32_t mTransform;
+
+ // mScalingMode is the current scaling mode for this buffer slot.
+ // refer to NATIVE_WINDOW_SCALING_* in <window.h>
+ uint32_t mScalingMode;
+
+ // mTimestamp is the current timestamp for this buffer slot. This gets
+ // to set by queueBuffer each time this slot is queued. This value
+ // is guaranteed to be monotonically increasing for each newly
+ // acquired buffer.
+ int64_t mTimestamp;
+
+ // mIsAutoTimestamp indicates whether mTimestamp was generated
+ // automatically when the buffer was queued.
+ bool mIsAutoTimestamp;
+
+ // mFrameNumber is the number of the queued frame for this slot.
+ uint64_t mFrameNumber;
+
+ // mSlot is the slot index of this buffer (default INVALID_BUFFER_SLOT).
+ int mSlot;
+
+ // mIsDroppable whether this buffer was queued with the
+ // property that it can be replaced by a new buffer for the purpose of
+ // making sure dequeueBuffer() won't block.
+ // i.e.: was the BufferQueue in "mDequeueBufferCannotBlock" when this buffer
+ // was queued.
+ bool mIsDroppable;
+
+ // Indicates whether this buffer has been seen by a consumer yet
+ bool mAcquireCalled;
+
+ // Indicates this buffer must be transformed by the inverse transform of the screen
+ // it is displayed onto. This is applied after mTransform.
+ bool mTransformToDisplayInverse;
+};
+
+} // namespace android
+
+#endif
diff --git a/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueConsumer.cpp b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueConsumer.cpp
new file mode 100644
index 000000000..1d7eb2702
--- /dev/null
+++ b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueConsumer.cpp
@@ -0,0 +1,559 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ * Copyright (C) 2014 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+
+#define LOG_TAG "GonkBufferQueueConsumer"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+//#define LOG_NDEBUG 0
+
+#include "GonkBufferItem.h"
+#include "GonkBufferQueueConsumer.h"
+#include "GonkBufferQueueCore.h"
+#include <gui/IConsumerListener.h>
+#include <gui/IProducerListener.h>
+
+namespace android {
+
+GonkBufferQueueConsumer::GonkBufferQueueConsumer(const sp<GonkBufferQueueCore>& core) :
+ mCore(core),
+ mSlots(core->mSlots),
+ mConsumerName() {}
+
+GonkBufferQueueConsumer::~GonkBufferQueueConsumer() {}
+
+status_t GonkBufferQueueConsumer::acquireBuffer(BufferItem* outBuffer,
+ nsecs_t expectedPresent) {
+ ATRACE_CALL();
+ Mutex::Autolock lock(mCore->mMutex);
+
+ // Check that the consumer doesn't currently have the maximum number of
+ // buffers acquired. We allow the max buffer count to be exceeded by one
+ // buffer so that the consumer can successfully set up the newly acquired
+ // buffer before releasing the old one.
+ int numAcquiredBuffers = 0;
+ for (int s = 0; s < GonkBufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
+ if (mSlots[s].mBufferState == GonkBufferSlot::ACQUIRED) {
+ ++numAcquiredBuffers;
+ }
+ }
+ if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1) {
+ ALOGE("acquireBuffer: max acquired buffer count reached: %d (max %d)",
+ numAcquiredBuffers, mCore->mMaxAcquiredBufferCount);
+ return INVALID_OPERATION;
+ }
+
+ // Check if the queue is empty.
+ // In asynchronous mode the list is guaranteed to be one buffer deep,
+ // while in synchronous mode we use the oldest buffer.
+ if (mCore->mQueue.empty()) {
+ return NO_BUFFER_AVAILABLE;
+ }
+
+ GonkBufferQueueCore::Fifo::iterator front(mCore->mQueue.begin());
+
+ // If expectedPresent is specified, we may not want to return a buffer yet.
+ // If it's specified and there's more than one buffer queued, we may want
+ // to drop a buffer.
+ if (expectedPresent != 0) {
+ const int MAX_REASONABLE_NSEC = 1000000000ULL; // 1 second
+
+ // The 'expectedPresent' argument indicates when the buffer is expected
+ // to be presented on-screen. If the buffer's desired present time is
+ // earlier (less) than expectedPresent -- meaning it will be displayed
+ // on time or possibly late if we show it as soon as possible -- we
+ // acquire and return it. If we don't want to display it until after the
+ // expectedPresent time, we return PRESENT_LATER without acquiring it.
+ //
+ // To be safe, we don't defer acquisition if expectedPresent is more
+ // than one second in the future beyond the desired present time
+ // (i.e., we'd be holding the buffer for a long time).
+ //
+ // NOTE: Code assumes monotonic time values from the system clock
+ // are positive.
+
+ // Start by checking to see if we can drop frames. We skip this check if
+ // the timestamps are being auto-generated by Surface. If the app isn't
+ // generating timestamps explicitly, it probably doesn't want frames to
+ // be discarded based on them.
+ while (mCore->mQueue.size() > 1 && !mCore->mQueue[0].mIsAutoTimestamp) {
+ // If entry[1] is timely, drop entry[0] (and repeat). We apply an
+ // additional criterion here: we only drop the earlier buffer if our
+ // desiredPresent falls within +/- 1 second of the expected present.
+ // Otherwise, bogus desiredPresent times (e.g., 0 or a small
+ // relative timestamp), which normally mean "ignore the timestamp
+ // and acquire immediately", would cause us to drop frames.
+ //
+ // We may want to add an additional criterion: don't drop the
+ // earlier buffer if entry[1]'s fence hasn't signaled yet.
+ const BufferItem& bufferItem(mCore->mQueue[1]);
+ nsecs_t desiredPresent = bufferItem.mTimestamp;
+ if (desiredPresent < expectedPresent - MAX_REASONABLE_NSEC ||
+ desiredPresent > expectedPresent) {
+ // This buffer is set to display in the near future, or
+ // desiredPresent is garbage. Either way we don't want to drop
+ // the previous buffer just to get this on the screen sooner.
+ ALOGV("acquireBuffer: nodrop desire=%" PRId64 " expect=%"
+ PRId64 " (%" PRId64 ") now=%" PRId64,
+ desiredPresent, expectedPresent,
+ desiredPresent - expectedPresent,
+ systemTime(CLOCK_MONOTONIC));
+ break;
+ }
+
+ ALOGV("acquireBuffer: drop desire=%" PRId64 " expect=%" PRId64
+ " size=%zu",
+ desiredPresent, expectedPresent, mCore->mQueue.size());
+ if (mCore->stillTracking(front)) {
+ // Front buffer is still in mSlots, so mark the slot as free
+ mSlots[front->mSlot].mBufferState = GonkBufferSlot::FREE;
+ }
+ mCore->mQueue.erase(front);
+ front = mCore->mQueue.begin();
+ }
+
+ // See if the front buffer is due
+ nsecs_t desiredPresent = front->mTimestamp;
+ if (desiredPresent > expectedPresent &&
+ desiredPresent < expectedPresent + MAX_REASONABLE_NSEC) {
+ ALOGV("acquireBuffer: defer desire=%" PRId64 " expect=%" PRId64
+ " (%" PRId64 ") now=%" PRId64,
+ desiredPresent, expectedPresent,
+ desiredPresent - expectedPresent,
+ systemTime(CLOCK_MONOTONIC));
+ return PRESENT_LATER;
+ }
+
+ ALOGV("acquireBuffer: accept desire=%" PRId64 " expect=%" PRId64 " "
+ "(%" PRId64 ") now=%" PRId64, desiredPresent, expectedPresent,
+ desiredPresent - expectedPresent,
+ systemTime(CLOCK_MONOTONIC));
+ }
+
+ int slot = front->mSlot;
+ //*outBuffer = *front;
+ outBuffer->mGraphicBuffer = mSlots[slot].mGraphicBuffer;
+ outBuffer->mFrameNumber = mSlots[slot].mFrameNumber;
+ outBuffer->mBuf = slot;
+ outBuffer->mFence = mSlots[slot].mFence;
+
+ ATRACE_BUFFER_INDEX(slot);
+
+ ALOGV("acquireBuffer: acquiring { slot=%d/%" PRIu64 " buffer=%p }",
+ slot, front->mFrameNumber, front->mGraphicBuffer->handle);
+ // If the front buffer is still being tracked, update its slot state
+ if (mCore->stillTracking(front)) {
+ mSlots[slot].mAcquireCalled = true;
+ mSlots[slot].mNeedsCleanupOnRelease = false;
+ mSlots[slot].mBufferState = GonkBufferSlot::ACQUIRED;
+ mSlots[slot].mFence = Fence::NO_FENCE;
+ }
+
+ // If the buffer has previously been acquired by the consumer, set
+ // mGraphicBuffer to NULL to avoid unnecessarily remapping this buffer
+ // on the consumer side
+ //if (outBuffer->mAcquireCalled) {
+ // outBuffer->mGraphicBuffer = NULL;
+ //}
+
+ mCore->mQueue.erase(front);
+
+ // We might have freed a slot while dropping old buffers, or the producer
+ // may be blocked waiting for the number of buffers in the queue to
+ // decrease.
+ mCore->mDequeueCondition.broadcast();
+
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueueConsumer::detachBuffer(int slot) {
+ ATRACE_CALL();
+ ATRACE_BUFFER_INDEX(slot);
+ ALOGV("detachBuffer(C): slot %d", slot);
+ Mutex::Autolock lock(mCore->mMutex);
+
+ if (mCore->mIsAbandoned) {
+ ALOGE("detachBuffer(C): GonkBufferQueue has been abandoned");
+ return NO_INIT;
+ }
+
+ if (slot < 0 || slot >= GonkBufferQueueDefs::NUM_BUFFER_SLOTS) {
+ ALOGE("detachBuffer(C): slot index %d out of range [0, %d)",
+ slot, GonkBufferQueueDefs::NUM_BUFFER_SLOTS);
+ return BAD_VALUE;
+ } else if (mSlots[slot].mBufferState != GonkBufferSlot::ACQUIRED) {
+ ALOGE("detachBuffer(C): slot %d is not owned by the consumer "
+ "(state = %d)", slot, mSlots[slot].mBufferState);
+ return BAD_VALUE;
+ }
+
+ mCore->freeBufferLocked(slot);
+ mCore->mDequeueCondition.broadcast();
+
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueueConsumer::attachBuffer(int* outSlot,
+ const sp<android::GraphicBuffer>& buffer) {
+ ATRACE_CALL();
+
+ if (outSlot == NULL) {
+ ALOGE("attachBuffer(P): outSlot must not be NULL");
+ return BAD_VALUE;
+ } else if (buffer == NULL) {
+ ALOGE("attachBuffer(P): cannot attach NULL buffer");
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mCore->mMutex);
+
+ // Make sure we don't have too many acquired buffers and find a free slot
+ // to put the buffer into (the oldest if there are multiple).
+ int numAcquiredBuffers = 0;
+ int found = GonkBufferQueueCore::INVALID_BUFFER_SLOT;
+ for (int s = 0; s < GonkBufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
+ if (mSlots[s].mBufferState == GonkBufferSlot::ACQUIRED) {
+ ++numAcquiredBuffers;
+ } else if (mSlots[s].mBufferState == GonkBufferSlot::FREE) {
+ if (found == GonkBufferQueueCore::INVALID_BUFFER_SLOT ||
+ mSlots[s].mFrameNumber < mSlots[found].mFrameNumber) {
+ found = s;
+ }
+ }
+ }
+
+ if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1) {
+ ALOGE("attachBuffer(P): max acquired buffer count reached: %d "
+ "(max %d)", numAcquiredBuffers,
+ mCore->mMaxAcquiredBufferCount);
+ return INVALID_OPERATION;
+ }
+ if (found == GonkBufferQueueCore::INVALID_BUFFER_SLOT) {
+ ALOGE("attachBuffer(P): could not find free buffer slot");
+ return NO_MEMORY;
+ }
+
+ *outSlot = found;
+ ATRACE_BUFFER_INDEX(*outSlot);
+ ALOGV("attachBuffer(C): returning slot %d", *outSlot);
+
+ mSlots[*outSlot].mGraphicBuffer = buffer;
+ mSlots[*outSlot].mBufferState = GonkBufferSlot::ACQUIRED;
+ mSlots[*outSlot].mAttachedByConsumer = true;
+ mSlots[*outSlot].mNeedsCleanupOnRelease = false;
+ mSlots[*outSlot].mFence = Fence::NO_FENCE;
+ mSlots[*outSlot].mFrameNumber = 0;
+
+ // mAcquireCalled tells GonkBufferQueue that it doesn't need to send a valid
+ // GraphicBuffer pointer on the next acquireBuffer call, which decreases
+ // Binder traffic by not un/flattening the GraphicBuffer. However, it
+ // requires that the consumer maintain a cached copy of the slot <--> buffer
+ // mappings, which is why the consumer doesn't need the valid pointer on
+ // acquire.
+ //
+ // The StreamSplitter is one of the primary users of the attach/detach
+ // logic, and while it is running, all buffers it acquires are immediately
+ // detached, and all buffers it eventually releases are ones that were
+ // attached (as opposed to having been obtained from acquireBuffer), so it
+ // doesn't make sense to maintain the slot/buffer mappings, which would
+ // become invalid for every buffer during detach/attach. By setting this to
+ // false, the valid GraphicBuffer pointer will always be sent with acquire
+ // for attached buffers.
+ mSlots[*outSlot].mAcquireCalled = false;
+
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber,
+ const sp<Fence>& releaseFence) {
+ ATRACE_CALL();
+
+ if (slot < 0 || slot >= GonkBufferQueueDefs::NUM_BUFFER_SLOTS ||
+ releaseFence == NULL) {
+ return BAD_VALUE;
+ }
+
+ sp<IProducerListener> listener;
+ { // Autolock scope
+ Mutex::Autolock lock(mCore->mMutex);
+
+ // If the frame number has changed because the buffer has been reallocated,
+ // we can ignore this releaseBuffer for the old buffer
+ //if (frameNumber != mSlots[slot].mFrameNumber) {
+ // return STALE_BUFFER_SLOT;
+ //}
+
+ // Make sure this buffer hasn't been queued while acquired by the consumer
+ GonkBufferQueueCore::Fifo::iterator current(mCore->mQueue.begin());
+ while (current != mCore->mQueue.end()) {
+ if (current->mSlot == slot) {
+ ALOGE("releaseBuffer: buffer slot %d pending release is "
+ "currently queued", slot);
+ return BAD_VALUE;
+ }
+ ++current;
+ }
+
+ if (mSlots[slot].mBufferState == GonkBufferSlot::ACQUIRED) {
+ mSlots[slot].mFence = releaseFence;
+ mSlots[slot].mBufferState = GonkBufferSlot::FREE;
+ listener = mCore->mConnectedProducerListener;
+ ALOGV("releaseBuffer: releasing slot %d", slot);
+ } else if (mSlots[slot].mNeedsCleanupOnRelease) {
+ ALOGV("releaseBuffer: releasing a stale buffer slot %d "
+ "(state = %d)", slot, mSlots[slot].mBufferState);
+ mSlots[slot].mNeedsCleanupOnRelease = false;
+ return STALE_BUFFER_SLOT;
+ } else {
+ ALOGV("releaseBuffer: attempted to release buffer slot %d "
+ "but its state was %d", slot, mSlots[slot].mBufferState);
+ return BAD_VALUE;
+ }
+
+ mCore->mDequeueCondition.broadcast();
+ } // Autolock scope
+
+ // Call back without lock held
+ if (listener != NULL) {
+ listener->onBufferReleased();
+ }
+
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueueConsumer::connect(
+ const sp<IConsumerListener>& consumerListener, bool controlledByApp) {
+ ATRACE_CALL();
+
+ if (consumerListener == NULL) {
+ ALOGE("connect(C): consumerListener may not be NULL");
+ return BAD_VALUE;
+ }
+
+ ALOGV("connect(C): controlledByApp=%s",
+ controlledByApp ? "true" : "false");
+
+ Mutex::Autolock lock(mCore->mMutex);
+
+ if (mCore->mIsAbandoned) {
+ ALOGE("connect(C): GonkBufferQueue has been abandoned");
+ return NO_INIT;
+ }
+
+ mCore->mConsumerListener = consumerListener;
+ mCore->mConsumerControlledByApp = controlledByApp;
+
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueueConsumer::disconnect() {
+ ATRACE_CALL();
+
+ ALOGV("disconnect(C)");
+
+ Mutex::Autolock lock(mCore->mMutex);
+
+ if (mCore->mConsumerListener == NULL) {
+ ALOGE("disconnect(C): no consumer is connected");
+ return BAD_VALUE;
+ }
+
+ mCore->mIsAbandoned = true;
+ mCore->mConsumerListener = NULL;
+ mCore->mQueue.clear();
+ mCore->freeAllBuffersLocked();
+ mCore->mDequeueCondition.broadcast();
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueueConsumer::getReleasedBuffers(uint64_t *outSlotMask) {
+ ATRACE_CALL();
+
+ if (outSlotMask == NULL) {
+ ALOGE("getReleasedBuffers: outSlotMask may not be NULL");
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mCore->mMutex);
+
+ if (mCore->mIsAbandoned) {
+ ALOGE("getReleasedBuffers: GonkBufferQueue has been abandoned");
+ return NO_INIT;
+ }
+
+ uint64_t mask = 0;
+ for (int s = 0; s < GonkBufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
+ if (!mSlots[s].mAcquireCalled) {
+ mask |= (1ULL << s);
+ }
+ }
+
+ // Remove from the mask queued buffers for which acquire has been called,
+ // since the consumer will not receive their buffer addresses and so must
+ // retain their cached information
+ GonkBufferQueueCore::Fifo::iterator current(mCore->mQueue.begin());
+ while (current != mCore->mQueue.end()) {
+ if (current->mAcquireCalled) {
+ mask &= ~(1ULL << current->mSlot);
+ }
+ ++current;
+ }
+
+ ALOGV("getReleasedBuffers: returning mask %#" PRIx64, mask);
+ *outSlotMask = mask;
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueueConsumer::setDefaultBufferSize(uint32_t width,
+ uint32_t height) {
+ ATRACE_CALL();
+
+ if (width == 0 || height == 0) {
+ ALOGV("setDefaultBufferSize: dimensions cannot be 0 (width=%u "
+ "height=%u)", width, height);
+ return BAD_VALUE;
+ }
+
+ ALOGV("setDefaultBufferSize: width=%u height=%u", width, height);
+
+ Mutex::Autolock lock(mCore->mMutex);
+ mCore->mDefaultWidth = width;
+ mCore->mDefaultHeight = height;
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueueConsumer::setDefaultMaxBufferCount(int bufferCount) {
+ ATRACE_CALL();
+ Mutex::Autolock lock(mCore->mMutex);
+ return mCore->setDefaultMaxBufferCountLocked(bufferCount);
+}
+
+status_t GonkBufferQueueConsumer::disableAsyncBuffer() {
+ ATRACE_CALL();
+
+ Mutex::Autolock lock(mCore->mMutex);
+
+ if (mCore->mConsumerListener != NULL) {
+ ALOGE("disableAsyncBuffer: consumer already connected");
+ return INVALID_OPERATION;
+ }
+
+ ALOGV("disableAsyncBuffer");
+ mCore->mUseAsyncBuffer = false;
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueueConsumer::setMaxAcquiredBufferCount(
+ int maxAcquiredBuffers) {
+ ATRACE_CALL();
+
+ if (maxAcquiredBuffers < 1 ||
+ maxAcquiredBuffers > GonkBufferQueueCore::MAX_MAX_ACQUIRED_BUFFERS) {
+ ALOGE("setMaxAcquiredBufferCount: invalid count %d",
+ maxAcquiredBuffers);
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mCore->mMutex);
+
+ if (mCore->mConnectedApi != GonkBufferQueueCore::NO_CONNECTED_API) {
+ ALOGE("setMaxAcquiredBufferCount: producer is already connected");
+ return INVALID_OPERATION;
+ }
+
+ ALOGV("setMaxAcquiredBufferCount: %d", maxAcquiredBuffers);
+ mCore->mMaxAcquiredBufferCount = maxAcquiredBuffers;
+ return NO_ERROR;
+}
+
+void GonkBufferQueueConsumer::setConsumerName(const String8& name) {
+ ATRACE_CALL();
+ ALOGV("setConsumerName: '%s'", name.string());
+ Mutex::Autolock lock(mCore->mMutex);
+ mCore->mConsumerName = name;
+ mConsumerName = name;
+}
+
+status_t GonkBufferQueueConsumer::setDefaultBufferFormat(uint32_t defaultFormat) {
+ ATRACE_CALL();
+ ALOGV("setDefaultBufferFormat: %u", defaultFormat);
+ Mutex::Autolock lock(mCore->mMutex);
+ mCore->mDefaultBufferFormat = defaultFormat;
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueueConsumer::setConsumerUsageBits(uint32_t usage) {
+ ATRACE_CALL();
+ ALOGV("setConsumerUsageBits: %#x", usage);
+ Mutex::Autolock lock(mCore->mMutex);
+ mCore->mConsumerUsageBits = usage;
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueueConsumer::setTransformHint(uint32_t hint) {
+ ATRACE_CALL();
+ ALOGV("setTransformHint: %#x", hint);
+ Mutex::Autolock lock(mCore->mMutex);
+ mCore->mTransformHint = hint;
+ return NO_ERROR;
+}
+
+sp<NativeHandle> GonkBufferQueueConsumer::getSidebandStream() const {
+ return mCore->mSidebandStream;
+}
+
+void GonkBufferQueueConsumer::dumpToString(String8& result, const char* prefix) const {
+ mCore->dump(result, prefix);
+}
+
+already_AddRefed<GonkBufferSlot::TextureClient>
+GonkBufferQueueConsumer::getTextureClientFromBuffer(ANativeWindowBuffer* buffer)
+{
+ Mutex::Autolock _l(mCore->mMutex);
+ if (buffer == NULL) {
+ ALOGE("getSlotFromBufferLocked: encountered NULL buffer");
+ return nullptr;
+ }
+
+ for (int i = 0; i < GonkBufferQueueDefs::NUM_BUFFER_SLOTS; i++) {
+ if (mSlots[i].mGraphicBuffer != NULL && mSlots[i].mGraphicBuffer->handle == buffer->handle) {
+ RefPtr<TextureClient> client(mSlots[i].mTextureClient);
+ return client.forget();
+ }
+ }
+ ALOGE("getSlotFromBufferLocked: unknown buffer: %p", buffer->handle);
+ return nullptr;
+}
+
+int
+GonkBufferQueueConsumer::getSlotFromTextureClientLocked(GonkBufferSlot::TextureClient* client) const
+{
+ if (client == NULL) {
+ ALOGE("getSlotFromBufferLocked: encountered NULL buffer");
+ return BAD_VALUE;
+ }
+
+ for (int i = 0; i < GonkBufferQueueDefs::NUM_BUFFER_SLOTS; i++) {
+ if (mSlots[i].mTextureClient == client) {
+ return i;
+ }
+ }
+ ALOGE("getSlotFromBufferLocked: unknown TextureClient: %p", client);
+ return BAD_VALUE;
+}
+
+} // namespace android
diff --git a/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueConsumer.h b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueConsumer.h
new file mode 100644
index 000000000..a97cfab42
--- /dev/null
+++ b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueConsumer.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ * Copyright (C) 2014 Mozilla Foundation
+ *
+ * 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 NATIVEWINDOW_GONKBUFFERQUEUECONSUMER_LL_H
+#define NATIVEWINDOW_GONKBUFFERQUEUECONSUMER_LL_H
+
+#include "GonkBufferQueueDefs.h"
+#include "IGonkGraphicBufferConsumerLL.h"
+
+namespace android {
+
+class GonkBufferQueueCore;
+
+class GonkBufferQueueConsumer : public BnGonkGraphicBufferConsumer {
+
+public:
+ GonkBufferQueueConsumer(const sp<GonkBufferQueueCore>& core);
+ virtual ~GonkBufferQueueConsumer();
+
+ // acquireBuffer attempts to acquire ownership of the next pending buffer in
+ // the GonkBufferQueue. If no buffer is pending then it returns
+ // NO_BUFFER_AVAILABLE. If a buffer is successfully acquired, the
+ // information about the buffer is returned in BufferItem. If the buffer
+ // returned had previously been acquired then the BufferItem::mGraphicBuffer
+ // field of buffer is set to NULL and it is assumed that the consumer still
+ // holds a reference to the buffer.
+ //
+ // If expectedPresent is nonzero, it indicates the time when the buffer
+ // will be displayed on screen. If the buffer's timestamp is farther in the
+ // future, the buffer won't be acquired, and PRESENT_LATER will be
+ // returned. The presentation time is in nanoseconds, and the time base
+ // is CLOCK_MONOTONIC.
+ virtual status_t acquireBuffer(BufferItem* outBuffer,
+ nsecs_t expectedPresent);
+
+ // See IGonkGraphicBufferConsumer::detachBuffer
+ virtual status_t detachBuffer(int slot);
+
+ // See IGonkGraphicBufferConsumer::attachBuffer
+ virtual status_t attachBuffer(int* slot, const sp<GraphicBuffer>& buffer);
+
+ // releaseBuffer releases a buffer slot from the consumer back to the
+ // GonkBufferQueue. This may be done while the buffer's contents are still
+ // being accessed. The fence will signal when the buffer is no longer
+ // in use. frameNumber is used to indentify the exact buffer returned.
+ //
+ // If releaseBuffer returns STALE_BUFFER_SLOT, then the consumer must free
+ // any references to the just-released buffer that it might have, as if it
+ // had received a onBuffersReleased() call with a mask set for the released
+ // buffer.
+ virtual status_t releaseBuffer(int slot, uint64_t frameNumber,
+ const sp<Fence>& releaseFence);
+
+ // connect connects a consumer to the GonkBufferQueue. Only one
+ // consumer may be connected, and when that consumer disconnects the
+ // GonkBufferQueue is placed into the "abandoned" state, causing most
+ // interactions with the GonkBufferQueue by the producer to fail.
+ // controlledByApp indicates whether the consumer is controlled by
+ // the application.
+ //
+ // consumerListener may not be NULL.
+ virtual status_t connect(const sp<IConsumerListener>& consumerListener,
+ bool controlledByApp);
+
+ // disconnect disconnects a consumer from the GonkBufferQueue. All
+ // buffers will be freed and the GonkBufferQueue is placed in the "abandoned"
+ // state, causing most interactions with the GonkBufferQueue by the producer to
+ // fail.
+ virtual status_t disconnect();
+
+ // getReleasedBuffers sets the value pointed to by outSlotMask to a bit mask
+ // indicating which buffer slots have been released by the GonkBufferQueue
+ // but have not yet been released by the consumer.
+ //
+ // This should be called from the onBuffersReleased() callback.
+ virtual status_t getReleasedBuffers(uint64_t* outSlotMask);
+
+ // setDefaultBufferSize is used to set the size of buffers returned by
+ // dequeueBuffer when a width and height of zero is requested. Default
+ // is 1x1.
+ virtual status_t setDefaultBufferSize(uint32_t width, uint32_t height);
+
+ // setDefaultMaxBufferCount sets the default value for the maximum buffer
+ // count (the initial default is 2). If the producer has requested a
+ // buffer count using setBufferCount, the default buffer count will only
+ // take effect if the producer sets the count back to zero.
+ //
+ // The count must be between 2 and NUM_BUFFER_SLOTS, inclusive.
+ virtual status_t setDefaultMaxBufferCount(int bufferCount);
+
+ // disableAsyncBuffer disables the extra buffer used in async mode
+ // (when both producer and consumer have set their "isControlledByApp"
+ // flag) and has dequeueBuffer() return WOULD_BLOCK instead.
+ //
+ // This can only be called before connect().
+ virtual status_t disableAsyncBuffer();
+
+ // setMaxAcquiredBufferCount sets the maximum number of buffers that can
+ // be acquired by the consumer at one time (default 1). This call will
+ // fail if a producer is connected to the GonkBufferQueue.
+ virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers);
+
+ // setConsumerName sets the name used in logging
+ virtual void setConsumerName(const String8& name);
+
+ // setDefaultBufferFormat allows the GonkBufferQueue to create
+ // GraphicBuffers of a defaultFormat if no format is specified
+ // in dequeueBuffer. Formats are enumerated in graphics.h; the
+ // initial default is HAL_PIXEL_FORMAT_RGBA_8888.
+ virtual status_t setDefaultBufferFormat(uint32_t defaultFormat);
+
+ // setConsumerUsageBits will turn on additional usage bits for dequeueBuffer.
+ // These are merged with the bits passed to dequeueBuffer. The values are
+ // enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER; the default is 0.
+ virtual status_t setConsumerUsageBits(uint32_t usage);
+
+ // setTransformHint bakes in rotation to buffers so overlays can be used.
+ // The values are enumerated in window.h, e.g.
+ // NATIVE_WINDOW_TRANSFORM_ROT_90. The default is 0 (no transform).
+ virtual status_t setTransformHint(uint32_t hint);
+
+ // Retrieve the sideband buffer stream, if any.
+ virtual sp<NativeHandle> getSidebandStream() const;
+
+ // dump our state in a String
+ virtual void dumpToString(String8& result, const char* prefix) const;
+
+ // Added by mozilla
+ virtual already_AddRefed<GonkBufferSlot::TextureClient> getTextureClientFromBuffer(ANativeWindowBuffer* buffer);
+
+ virtual int getSlotFromTextureClientLocked(GonkBufferSlot::TextureClient* client) const;
+
+ // Functions required for backwards compatibility.
+ // These will be modified/renamed in IGonkGraphicBufferConsumer and will be
+ // removed from this class at that time. See b/13306289.
+ virtual status_t consumerConnect(const sp<IConsumerListener>& consumer,
+ bool controlledByApp) {
+ return connect(consumer, controlledByApp);
+ }
+
+ virtual status_t consumerDisconnect() { return disconnect(); }
+
+ // End functions required for backwards compatibility
+
+private:
+ sp<GonkBufferQueueCore> mCore;
+
+ // This references mCore->mSlots. Lock mCore->mMutex while accessing.
+ GonkBufferQueueDefs::SlotsType& mSlots;
+
+ // This is a cached copy of the name stored in the GonkBufferQueueCore.
+ // It's updated during setConsumerName.
+ String8 mConsumerName;
+
+}; // class GonkBufferQueueConsumer
+
+} // namespace android
+
+#endif
diff --git a/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueCore.cpp b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueCore.cpp
new file mode 100644
index 000000000..9e8e337f6
--- /dev/null
+++ b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueCore.cpp
@@ -0,0 +1,243 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ * Copyright (C) 2014 Mozilla Foundation
+ *
+ * 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 "GonkBufferQueueCore"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+//#define LOG_NDEBUG 0
+
+#include <inttypes.h>
+
+#include "GonkBufferItem.h"
+#include "GonkBufferQueueCore.h"
+#include <gui/IConsumerListener.h>
+#include <gui/IGraphicBufferAlloc.h>
+#include <gui/IProducerListener.h>
+#include <gui/ISurfaceComposer.h>
+#include <private/gui/ComposerService.h>
+
+#include <cutils/compiler.h>
+#include "mozilla/layers/GrallocTextureClient.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+
+template <typename T>
+static inline T max(T a, T b) { return a > b ? a : b; }
+
+namespace android {
+
+static String8 getUniqueName() {
+ static volatile int32_t counter = 0;
+ return String8::format("unnamed-%d-%d", getpid(),
+ android_atomic_inc(&counter));
+}
+
+GonkBufferQueueCore::GonkBufferQueueCore(const sp<IGraphicBufferAlloc>& allocator) :
+ mAllocator(allocator),
+ mMutex(),
+ mIsAbandoned(false),
+ mConsumerControlledByApp(false),
+ mConsumerName(getUniqueName()),
+ mConsumerListener(),
+ mConsumerUsageBits(0),
+ mConnectedApi(NO_CONNECTED_API),
+ mConnectedProducerListener(),
+ mSlots(),
+ mQueue(),
+ mOverrideMaxBufferCount(0),
+ mDequeueCondition(),
+ mUseAsyncBuffer(true),
+ mDequeueBufferCannotBlock(false),
+ mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888),
+ mDefaultWidth(1),
+ mDefaultHeight(1),
+ mDefaultMaxBufferCount(2),
+ mMaxAcquiredBufferCount(1),
+ mBufferHasBeenQueued(false),
+ mFrameCounter(0),
+ mTransformHint(0),
+ mIsAllocating(false),
+ mIsAllocatingCondition()
+{
+ ALOGV("GonkBufferQueueCore");
+}
+
+GonkBufferQueueCore::~GonkBufferQueueCore() {}
+
+void GonkBufferQueueCore::dump(String8& result, const char* prefix) const {
+ Mutex::Autolock lock(mMutex);
+
+ String8 fifo;
+ Fifo::const_iterator current(mQueue.begin());
+ while (current != mQueue.end()) {
+ fifo.appendFormat("%02d:%p crop=[%d,%d,%d,%d], "
+ "xform=0x%02x, time=%#" PRIx64 ", scale=%s\n",
+ current->mSlot, current->mGraphicBuffer.get(),
+ current->mCrop.left, current->mCrop.top, current->mCrop.right,
+ current->mCrop.bottom, current->mTransform, current->mTimestamp,
+ GonkBufferItem::scalingModeName(current->mScalingMode));
+ ++current;
+ }
+
+ result.appendFormat("%s-GonkBufferQueue mMaxAcquiredBufferCount=%d, "
+ "mDequeueBufferCannotBlock=%d, default-size=[%dx%d], "
+ "default-format=%d, transform-hint=%02x, FIFO(%zu)={%s}\n",
+ prefix, mMaxAcquiredBufferCount, mDequeueBufferCannotBlock,
+ mDefaultWidth, mDefaultHeight, mDefaultBufferFormat, mTransformHint,
+ mQueue.size(), fifo.string());
+
+ // Trim the free buffers so as to not spam the dump
+ int maxBufferCount = 0;
+ for (int s = GonkBufferQueueDefs::NUM_BUFFER_SLOTS - 1; s >= 0; --s) {
+ const GonkBufferSlot& slot(mSlots[s]);
+ if (slot.mBufferState != GonkBufferSlot::FREE ||
+ slot.mGraphicBuffer != NULL) {
+ maxBufferCount = s + 1;
+ break;
+ }
+ }
+
+ for (int s = 0; s < maxBufferCount; ++s) {
+ const GonkBufferSlot& slot(mSlots[s]);
+ const sp<GraphicBuffer>& buffer(slot.mGraphicBuffer);
+ result.appendFormat("%s%s[%02d:%p] state=%-8s", prefix,
+ (slot.mBufferState == GonkBufferSlot::ACQUIRED) ? ">" : " ",
+ s, buffer.get(),
+ GonkBufferSlot::bufferStateName(slot.mBufferState));
+
+ if (buffer != NULL) {
+ result.appendFormat(", %p [%4ux%4u:%4u,%3X]", buffer->handle,
+ buffer->width, buffer->height, buffer->stride,
+ buffer->format);
+ }
+
+ result.append("\n");
+ }
+}
+
+int GonkBufferQueueCore::getMinUndequeuedBufferCountLocked(bool async) const {
+ // If dequeueBuffer is allowed to error out, we don't have to add an
+ // extra buffer.
+ if (!mUseAsyncBuffer) {
+ return mMaxAcquiredBufferCount;
+ }
+
+ if (mDequeueBufferCannotBlock || async) {
+ return mMaxAcquiredBufferCount + 1;
+ }
+
+ return mMaxAcquiredBufferCount;
+}
+
+int GonkBufferQueueCore::getMinMaxBufferCountLocked(bool async) const {
+ return getMinUndequeuedBufferCountLocked(async) + 1;
+}
+
+int GonkBufferQueueCore::getMaxBufferCountLocked(bool async) const {
+ int minMaxBufferCount = getMinMaxBufferCountLocked(async);
+
+ int maxBufferCount = max(mDefaultMaxBufferCount, minMaxBufferCount);
+ if (mOverrideMaxBufferCount != 0) {
+ assert(mOverrideMaxBufferCount >= minMaxBufferCount);
+ maxBufferCount = mOverrideMaxBufferCount;
+ }
+
+ // Any buffers that are dequeued by the producer or sitting in the queue
+ // waiting to be consumed need to have their slots preserved. Such buffers
+ // will temporarily keep the max buffer count up until the slots no longer
+ // need to be preserved.
+ for (int s = maxBufferCount; s < GonkBufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
+ GonkBufferSlot::BufferState state = mSlots[s].mBufferState;
+ if (state == GonkBufferSlot::QUEUED || state == GonkBufferSlot::DEQUEUED) {
+ maxBufferCount = s + 1;
+ }
+ }
+
+ return maxBufferCount;
+}
+
+status_t GonkBufferQueueCore::setDefaultMaxBufferCountLocked(int count) {
+ const int minBufferCount = 2;
+ if (count < minBufferCount || count > GonkBufferQueueDefs::NUM_BUFFER_SLOTS) {
+ ALOGV("setDefaultMaxBufferCount: invalid count %d, should be in "
+ "[%d, %d]",
+ count, minBufferCount, GonkBufferQueueDefs::NUM_BUFFER_SLOTS);
+ return BAD_VALUE;
+ }
+
+ ALOGV("setDefaultMaxBufferCount: setting count to %d", count);
+ mDefaultMaxBufferCount = count;
+ mDequeueCondition.broadcast();
+
+ return NO_ERROR;
+}
+
+void GonkBufferQueueCore::freeBufferLocked(int slot) {
+ ALOGV("freeBufferLocked: slot %d", slot);
+
+ if (mSlots[slot].mTextureClient) {
+ mSlots[slot].mTextureClient->ClearRecycleCallback();
+ // release TextureClient in ImageBridge thread
+ RefPtr<TextureClientReleaseTask> task =
+ MakeAndAddRef<TextureClientReleaseTask>(mSlots[slot].mTextureClient);
+ mSlots[slot].mTextureClient = NULL;
+ ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(task.forget());
+ }
+ mSlots[slot].mGraphicBuffer.clear();
+ if (mSlots[slot].mBufferState == GonkBufferSlot::ACQUIRED) {
+ mSlots[slot].mNeedsCleanupOnRelease = true;
+ }
+ mSlots[slot].mBufferState = GonkBufferSlot::FREE;
+ mSlots[slot].mFrameNumber = UINT32_MAX;
+ mSlots[slot].mAcquireCalled = false;
+
+ // Destroy fence as GonkBufferQueue now takes ownership
+ mSlots[slot].mFence = Fence::NO_FENCE;
+}
+
+void GonkBufferQueueCore::freeAllBuffersLocked() {
+ ALOGW_IF(!mQueue.isEmpty(),
+ "freeAllBuffersLocked called but mQueue is not empty");
+ mQueue.clear();
+ mBufferHasBeenQueued = false;
+ for (int s = 0; s < GonkBufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
+ freeBufferLocked(s);
+ }
+}
+
+bool GonkBufferQueueCore::stillTracking(const GonkBufferItem* item) const {
+ const GonkBufferSlot& slot = mSlots[item->mSlot];
+
+ ALOGV("stillTracking: item { slot=%d/%" PRIu64 " buffer=%p } "
+ "slot { slot=%d/%" PRIu64 " buffer=%p }",
+ item->mSlot, item->mFrameNumber,
+ (item->mGraphicBuffer.get() ? item->mGraphicBuffer->handle : 0),
+ item->mSlot, slot.mFrameNumber,
+ (slot.mGraphicBuffer.get() ? slot.mGraphicBuffer->handle : 0));
+
+ // Compare item with its original buffer slot. We can check the slot as
+ // the buffer would not be moved to a different slot by the producer.
+ return (slot.mGraphicBuffer != NULL) &&
+ (item->mGraphicBuffer->handle == slot.mGraphicBuffer->handle);
+}
+
+void GonkBufferQueueCore::waitWhileAllocatingLocked() const {
+ ATRACE_CALL();
+ while (mIsAllocating) {
+ mIsAllocatingCondition.wait(mMutex);
+ }
+}
+
+} // namespace android
diff --git a/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueCore.h b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueCore.h
new file mode 100644
index 000000000..936e11686
--- /dev/null
+++ b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueCore.h
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ * Copyright (C) 2014 Mozilla Foundation
+ *
+ * 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 NATIVEWINDOW_GONKBUFFERQUEUECORE_LL_H
+#define NATIVEWINDOW_GONKBUFFERQUEUECORE_LL_H
+
+#include "GonkBufferQueueDefs.h"
+#include "GonkBufferSlot.h"
+
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
+#include <utils/NativeHandle.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+#include <utils/StrongPointer.h>
+#include <utils/Trace.h>
+#include <utils/Vector.h>
+
+#include "mozilla/layers/TextureClient.h"
+
+#define ATRACE_BUFFER_INDEX(index)
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+
+namespace android {
+
+class GonkBufferItem;
+class IConsumerListener;
+class IGraphicBufferAlloc;
+class IProducerListener;
+
+class GonkBufferQueueCore : public virtual RefBase {
+
+ friend class GonkBufferQueueProducer;
+ friend class GonkBufferQueueConsumer;
+
+public:
+ // Used as a placeholder slot number when the value isn't pointing to an
+ // existing buffer.
+ enum { INVALID_BUFFER_SLOT = -1 }; // TODO: Extract from IGBC::BufferItem
+
+ // We reserve two slots in order to guarantee that the producer and
+ // consumer can run asynchronously.
+ enum { MAX_MAX_ACQUIRED_BUFFERS = GonkBufferQueueDefs::NUM_BUFFER_SLOTS - 2 };
+
+ // The default API number used to indicate that no producer is connected
+ enum { NO_CONNECTED_API = 0 };
+
+ typedef Vector<GonkBufferItem> Fifo;
+ typedef mozilla::layers::TextureClient TextureClient;
+
+ // GonkBufferQueueCore manages a pool of gralloc memory slots to be used by
+ // producers and consumers. allocator is used to allocate all the needed
+ // gralloc buffers.
+ GonkBufferQueueCore(const sp<IGraphicBufferAlloc>& allocator = NULL);
+ virtual ~GonkBufferQueueCore();
+
+private:
+ // Dump our state in a string
+ void dump(String8& result, const char* prefix) const;
+
+ int getSlotFromTextureClientLocked(TextureClient* client) const;
+
+ // getMinUndequeuedBufferCountLocked returns the minimum number of buffers
+ // that must remain in a state other than DEQUEUED. The async parameter
+ // tells whether we're in asynchronous mode.
+ int getMinUndequeuedBufferCountLocked(bool async) const;
+
+ // getMinMaxBufferCountLocked returns the minimum number of buffers allowed
+ // given the current GonkBufferQueue state. The async parameter tells whether
+ // we're in asynchonous mode.
+ int getMinMaxBufferCountLocked(bool async) const;
+
+ // getMaxBufferCountLocked returns the maximum number of buffers that can be
+ // allocated at once. This value depends on the following member variables:
+ //
+ // mDequeueBufferCannotBlock
+ // mMaxAcquiredBufferCount
+ // mDefaultMaxBufferCount
+ // mOverrideMaxBufferCount
+ // async parameter
+ //
+ // Any time one of these member variables is changed while a producer is
+ // connected, mDequeueCondition must be broadcast.
+ int getMaxBufferCountLocked(bool async) const;
+
+ // setDefaultMaxBufferCountLocked sets the maximum number of buffer slots
+ // that will be used if the producer does not override the buffer slot
+ // count. The count must be between 2 and NUM_BUFFER_SLOTS, inclusive. The
+ // initial default is 2.
+ status_t setDefaultMaxBufferCountLocked(int count);
+
+ // freeBufferLocked frees the GraphicBuffer and sync resources for the
+ // given slot.
+ void freeBufferLocked(int slot);
+
+ // freeAllBuffersLocked frees the GraphicBuffer and sync resources for
+ // all slots.
+ void freeAllBuffersLocked();
+
+ // stillTracking returns true iff the buffer item is still being tracked
+ // in one of the slots.
+ bool stillTracking(const GonkBufferItem* item) const;
+
+ // waitWhileAllocatingLocked blocks until mIsAllocating is false.
+ void waitWhileAllocatingLocked() const;
+
+ // mAllocator is the connection to SurfaceFlinger that is used to allocate
+ // new GraphicBuffer objects.
+ sp<IGraphicBufferAlloc> mAllocator;
+
+ // mMutex is the mutex used to prevent concurrent access to the member
+ // variables of GonkBufferQueueCore objects. It must be locked whenever any
+ // member variable is accessed.
+ mutable Mutex mMutex;
+
+ // mIsAbandoned indicates that the GonkBufferQueue will no longer be used to
+ // consume image buffers pushed to it using the IGraphicBufferProducer
+ // interface. It is initialized to false, and set to true in the
+ // consumerDisconnect method. A GonkBufferQueue that is abandoned will return
+ // the NO_INIT error from all IGraphicBufferProducer methods capable of
+ // returning an error.
+ bool mIsAbandoned;
+
+ // mConsumerControlledByApp indicates whether the connected consumer is
+ // controlled by the application.
+ bool mConsumerControlledByApp;
+
+ // mConsumerName is a string used to identify the GonkBufferQueue in log
+ // messages. It is set by the IGraphicBufferConsumer::setConsumerName
+ // method.
+ String8 mConsumerName;
+
+ // mConsumerListener is used to notify the connected consumer of
+ // asynchronous events that it may wish to react to. It is initially
+ // set to NULL and is written by consumerConnect and consumerDisconnect.
+ sp<IConsumerListener> mConsumerListener;
+
+ // mConsumerUsageBits contains flags that the consumer wants for
+ // GraphicBuffers.
+ uint32_t mConsumerUsageBits;
+
+ // mConnectedApi indicates the producer API that is currently connected
+ // to this GonkBufferQueue. It defaults to NO_CONNECTED_API, and gets updated
+ // by the connect and disconnect methods.
+ int mConnectedApi;
+
+ // mConnectedProducerToken is used to set a binder death notification on
+ // the producer.
+ sp<IProducerListener> mConnectedProducerListener;
+
+ // mSlots is an array of buffer slots that must be mirrored on the producer
+ // side. This allows buffer ownership to be transferred between the producer
+ // and consumer 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.
+ GonkBufferQueueDefs::SlotsType mSlots;
+
+ // mQueue is a FIFO of queued buffers used in synchronous mode.
+ Fifo mQueue;
+
+ // mOverrideMaxBufferCount is the limit on the number of buffers that will
+ // be allocated at one time. This value is set by the producer by calling
+ // setBufferCount. The default is 0, which means that the producer doesn't
+ // care about the number of buffers in the pool. In that case,
+ // mDefaultMaxBufferCount is used as the limit.
+ int mOverrideMaxBufferCount;
+
+ // mDequeueCondition is a condition variable used for dequeueBuffer in
+ // synchronous mode.
+ mutable Condition mDequeueCondition;
+
+ // mUseAsyncBuffer indicates whether an extra buffer is used in async mode
+ // to prevent dequeueBuffer from blocking.
+ bool mUseAsyncBuffer;
+
+ // mDequeueBufferCannotBlock indicates whether dequeueBuffer is allowed to
+ // block. This flag is set during connect when both the producer and
+ // consumer are controlled by the application.
+ bool mDequeueBufferCannotBlock;
+
+ // mDefaultBufferFormat can be set so it will override the buffer format
+ // when it isn't specified in dequeueBuffer.
+ uint32_t mDefaultBufferFormat;
+
+ // mDefaultWidth holds the default width of allocated buffers. It is used
+ // in dequeueBuffer if a width and height of 0 are specified.
+ int mDefaultWidth;
+
+ // mDefaultHeight holds the default height of allocated buffers. It is used
+ // in dequeueBuffer if a width and height of 0 are specified.
+ int mDefaultHeight;
+
+ // mDefaultMaxBufferCount is the default limit on the number of buffers that
+ // will be allocated at one time. This default limit is set by the consumer.
+ // The limit (as opposed to the default limit) may be overriden by the
+ // producer.
+ int mDefaultMaxBufferCount;
+
+ // mMaxAcquiredBufferCount is the number of buffers that the consumer may
+ // acquire at one time. It defaults to 1, and can be changed by the consumer
+ // via setMaxAcquiredBufferCount, but this may only be done while no
+ // producer is connected to the GonkBufferQueue. This value is used to derive
+ // the value returned for the MIN_UNDEQUEUED_BUFFERS query to the producer.
+ int mMaxAcquiredBufferCount;
+
+ // mBufferHasBeenQueued is true once a buffer has been queued. It is reset
+ // when something causes all buffers to be freed (e.g., changing the buffer
+ // count).
+ bool mBufferHasBeenQueued;
+
+ // mFrameCounter is the free running counter, incremented on every
+ // successful queueBuffer call and buffer allocation.
+ uint64_t mFrameCounter;
+
+ // mTransformHint is used to optimize for screen rotations.
+ uint32_t mTransformHint;
+
+ // mSidebandStream is a handle to the sideband buffer stream, if any
+ sp<NativeHandle> mSidebandStream;
+
+ // mIsAllocating indicates whether a producer is currently trying to allocate buffers (which
+ // releases mMutex while doing the allocation proper). Producers should not modify any of the
+ // FREE slots while this is true. mIsAllocatingCondition is signaled when this value changes to
+ // false.
+ bool mIsAllocating;
+
+ // mIsAllocatingCondition is a condition variable used by producers to wait until mIsAllocating
+ // becomes false.
+ mutable Condition mIsAllocatingCondition;
+}; // class GonkBufferQueueCore
+
+} // namespace android
+
+#endif
diff --git a/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueDefs.h b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueDefs.h
new file mode 100644
index 000000000..60085706f
--- /dev/null
+++ b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueDefs.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ * Copyright (C) 2014 Mozilla Foundation
+ *
+ * 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 NATIVEWINDOW_BUFFERQUEUECOREDEFS_H
+#define NATIVEWINDOW_BUFFERQUEUECOREDEFS_H
+
+#include "GonkBufferSlot.h"
+
+namespace android {
+ class GonkBufferQueueCore;
+
+ namespace GonkBufferQueueDefs {
+ // GonkBufferQueue will keep track of at most this value of buffers.
+ // Attempts at runtime to increase the number of buffers past this
+ // will fail.
+ enum { NUM_BUFFER_SLOTS = 64 };
+
+ typedef GonkBufferSlot SlotsType[NUM_BUFFER_SLOTS];
+ } // namespace GonkBufferQueueDefs
+} // namespace android
+
+#endif
diff --git a/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueLL.cpp b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueLL.cpp
new file mode 100644
index 000000000..649d06bee
--- /dev/null
+++ b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueLL.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2014 Mozilla Foundation
+ *
+ * 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 "GonkBufferQueue"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#define LOG_NDEBUG 0
+
+#include "GonkBufferQueue.h"
+#include "GonkBufferQueueConsumer.h"
+#include "GonkBufferQueueCore.h"
+#include "GonkBufferQueueProducer.h"
+
+namespace android {
+
+GonkBufferQueue::ProxyConsumerListener::ProxyConsumerListener(
+ const wp<ConsumerListener>& consumerListener):
+ mConsumerListener(consumerListener) {}
+
+GonkBufferQueue::ProxyConsumerListener::~ProxyConsumerListener() {}
+
+#if ANDROID_VERSION == 21
+void GonkBufferQueue::ProxyConsumerListener::onFrameAvailable() {
+ sp<ConsumerListener> listener(mConsumerListener.promote());
+ if (listener != NULL) {
+ listener->onFrameAvailable();
+ }
+}
+#else
+void GonkBufferQueue::ProxyConsumerListener::onFrameAvailable(const ::android::BufferItem& item) {
+ sp<ConsumerListener> listener(mConsumerListener.promote());
+ if (listener != NULL) {
+ listener->onFrameAvailable(item);
+ }
+}
+
+void GonkBufferQueue::ProxyConsumerListener::onFrameReplaced(const ::android::BufferItem& item) {
+ sp<ConsumerListener> listener(mConsumerListener.promote());
+ if (listener != NULL) {
+ listener->onFrameReplaced(item);
+ }
+}
+#endif
+
+void GonkBufferQueue::ProxyConsumerListener::onBuffersReleased() {
+ sp<ConsumerListener> listener(mConsumerListener.promote());
+ if (listener != NULL) {
+ listener->onBuffersReleased();
+ }
+}
+
+void GonkBufferQueue::ProxyConsumerListener::onSidebandStreamChanged() {
+ sp<ConsumerListener> listener(mConsumerListener.promote());
+ if (listener != NULL) {
+ listener->onSidebandStreamChanged();
+ }
+}
+
+void GonkBufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
+ sp<IGonkGraphicBufferConsumer>* outConsumer,
+ const sp<IGraphicBufferAlloc>& allocator) {
+ LOG_ALWAYS_FATAL_IF(outProducer == NULL,
+ "GonkBufferQueue: outProducer must not be NULL");
+ LOG_ALWAYS_FATAL_IF(outConsumer == NULL,
+ "GonkBufferQueue: outConsumer must not be NULL");
+
+ sp<GonkBufferQueueCore> core(new GonkBufferQueueCore(allocator));
+ LOG_ALWAYS_FATAL_IF(core == NULL,
+ "GonkBufferQueue: failed to create GonkBufferQueueCore");
+
+ sp<IGraphicBufferProducer> producer(new GonkBufferQueueProducer(core));
+ LOG_ALWAYS_FATAL_IF(producer == NULL,
+ "GonkBufferQueue: failed to create GonkBufferQueueProducer");
+
+ sp<IGonkGraphicBufferConsumer> consumer(new GonkBufferQueueConsumer(core));
+ LOG_ALWAYS_FATAL_IF(consumer == NULL,
+ "GonkBufferQueue: failed to create GonkBufferQueueConsumer");
+
+ *outProducer = producer;
+ *outConsumer = consumer;
+}
+
+}; // namespace android
diff --git a/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueLL.h b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueLL.h
new file mode 100644
index 000000000..b1b4e06b5
--- /dev/null
+++ b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueLL.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2014 Mozilla Foundation
+ *
+ * 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 NATIVEWINDOW_GONKBUFFERQUEUE_LL_H
+#define NATIVEWINDOW_GONKBUFFERQUEUE_LL_H
+
+#include "GonkBufferQueueDefs.h"
+#include "IGonkGraphicBufferConsumerLL.h"
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/IConsumerListener.h>
+
+// These are only required to keep other parts of the framework with incomplete
+// dependencies building successfully
+#include <gui/IGraphicBufferAlloc.h>
+
+namespace android {
+
+class GonkBufferQueue {
+public:
+ // GonkBufferQueue will keep track of at most this value of buffers.
+ // Attempts at runtime to increase the number of buffers past this will fail.
+ enum { NUM_BUFFER_SLOTS = GonkBufferQueueDefs::NUM_BUFFER_SLOTS };
+ // Used as a placeholder slot# when the value isn't pointing to an existing buffer.
+ enum { INVALID_BUFFER_SLOT = IGonkGraphicBufferConsumer::BufferItem::INVALID_BUFFER_SLOT };
+ // Alias to <IGonkGraphicBufferConsumer.h> -- please scope from there in future code!
+ enum {
+ NO_BUFFER_AVAILABLE = IGonkGraphicBufferConsumer::NO_BUFFER_AVAILABLE,
+ PRESENT_LATER = IGonkGraphicBufferConsumer::PRESENT_LATER,
+ };
+
+ // When in async mode we reserve two slots in order to guarantee that the
+ // producer and consumer can run asynchronously.
+ enum { MAX_MAX_ACQUIRED_BUFFERS = NUM_BUFFER_SLOTS - 2 };
+
+ // for backward source compatibility
+ typedef ::android::ConsumerListener ConsumerListener;
+ typedef IGonkGraphicBufferConsumer::BufferItem BufferItem;
+
+ // ProxyConsumerListener is a ConsumerListener implementation that keeps a weak
+ // reference to the actual consumer object. It forwards all calls to that
+ // consumer object so long as it exists.
+ //
+ // This class exists to avoid having a circular reference between the
+ // GonkBufferQueue object and the consumer object. The reason this can't be a weak
+ // reference in the GonkBufferQueue class is because we're planning to expose the
+ // consumer side of a GonkBufferQueue as a binder interface, which doesn't support
+ // weak references.
+ class ProxyConsumerListener : public BnConsumerListener {
+ public:
+ ProxyConsumerListener(const wp<ConsumerListener>& consumerListener);
+ virtual ~ProxyConsumerListener();
+#if ANDROID_VERSION == 21
+ virtual void onFrameAvailable();
+#else
+ virtual void onFrameAvailable(const ::android::BufferItem& item);
+ virtual void onFrameReplaced(const ::android::BufferItem& item);
+#endif
+ virtual void onBuffersReleased();
+ virtual void onSidebandStreamChanged();
+ private:
+ // mConsumerListener is a weak reference to the IConsumerListener. This is
+ // the raison d'etre of ProxyConsumerListener.
+ wp<ConsumerListener> mConsumerListener;
+ };
+
+ // GonkBufferQueue manages a pool of gralloc memory slots to be used by
+ // producers and consumers. allocator is used to allocate all the
+ // needed gralloc buffers.
+ static void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
+ sp<IGonkGraphicBufferConsumer>* outConsumer,
+ const sp<IGraphicBufferAlloc>& allocator = NULL);
+
+private:
+ GonkBufferQueue(); // Create through createBufferQueue
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // NATIVEWINDOW_GONKBUFFERQUEUE_LL_H
diff --git a/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueProducer.cpp b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueProducer.cpp
new file mode 100644
index 000000000..d3436756f
--- /dev/null
+++ b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueProducer.cpp
@@ -0,0 +1,886 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ * Copyright (C) 2014 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+
+#define LOG_TAG "GonkBufferQueueProducer"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+//#define LOG_NDEBUG 0
+
+#include "GonkBufferItem.h"
+#include "GonkBufferQueueCore.h"
+#include "GonkBufferQueueProducer.h"
+#include <gui/IConsumerListener.h>
+#include <gui/IGraphicBufferAlloc.h>
+#include <gui/IProducerListener.h>
+
+#include <cutils/compiler.h>
+#include <utils/Log.h>
+#include <utils/Trace.h>
+
+#include "mozilla/layers/GrallocTextureClient.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+#include "mozilla/layers/TextureClient.h"
+
+namespace android {
+
+GonkBufferQueueProducer::GonkBufferQueueProducer(const sp<GonkBufferQueueCore>& core) :
+ mCore(core),
+ mSlots(core->mSlots),
+ mConsumerName(),
+ mSynchronousMode(true),
+ mStickyTransform(0) {}
+
+GonkBufferQueueProducer::~GonkBufferQueueProducer() {}
+
+status_t GonkBufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
+ ATRACE_CALL();
+ ALOGV("requestBuffer: slot %d", slot);
+ Mutex::Autolock lock(mCore->mMutex);
+
+ if (mCore->mIsAbandoned) {
+ ALOGE("requestBuffer: GonkBufferQueue has been abandoned");
+ return NO_INIT;
+ }
+
+ if (slot < 0 || slot >= GonkBufferQueueDefs::NUM_BUFFER_SLOTS) {
+ ALOGE("requestBuffer: slot index %d out of range [0, %d)",
+ slot, GonkBufferQueueDefs::NUM_BUFFER_SLOTS);
+ return BAD_VALUE;
+ } else if (mSlots[slot].mBufferState != GonkBufferSlot::DEQUEUED) {
+ ALOGE("requestBuffer: slot %d is not owned by the producer "
+ "(state = %d)", slot, mSlots[slot].mBufferState);
+ return BAD_VALUE;
+ }
+
+ mSlots[slot].mRequestBufferCalled = true;
+ *buf = mSlots[slot].mGraphicBuffer;
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueueProducer::setBufferCount(int bufferCount) {
+ ATRACE_CALL();
+ ALOGV("setBufferCount: count = %d", bufferCount);
+
+ sp<IConsumerListener> listener;
+ { // Autolock scope
+ Mutex::Autolock lock(mCore->mMutex);
+ mCore->waitWhileAllocatingLocked();
+
+ if (mCore->mIsAbandoned) {
+ ALOGE("setBufferCount: GonkBufferQueue has been abandoned");
+ return NO_INIT;
+ }
+
+ if (bufferCount > GonkBufferQueueDefs::NUM_BUFFER_SLOTS) {
+ ALOGE("setBufferCount: bufferCount %d too large (max %d)",
+ bufferCount, GonkBufferQueueDefs::NUM_BUFFER_SLOTS);
+ return BAD_VALUE;
+ }
+
+ // There must be no dequeued buffers when changing the buffer count.
+ for (int s = 0; s < GonkBufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
+ if (mSlots[s].mBufferState == GonkBufferSlot::DEQUEUED) {
+ ALOGE("setBufferCount: buffer owned by producer");
+ return BAD_VALUE;
+ }
+ }
+
+ if (bufferCount == 0) {
+ mCore->mOverrideMaxBufferCount = 0;
+ mCore->mDequeueCondition.broadcast();
+ return NO_ERROR;
+ }
+
+ const int minBufferSlots = mCore->getMinMaxBufferCountLocked(false);
+ if (bufferCount < minBufferSlots) {
+ ALOGE("setBufferCount: requested buffer count %d is less than "
+ "minimum %d", bufferCount, minBufferSlots);
+ return BAD_VALUE;
+ }
+
+ // Here we are guaranteed that the producer doesn't have any dequeued
+ // buffers and will release all of its buffer references. We don't
+ // clear the queue, however, so that currently queued buffers still
+ // get displayed.
+ mCore->freeAllBuffersLocked();
+ mCore->mOverrideMaxBufferCount = bufferCount;
+ mCore->mDequeueCondition.broadcast();
+ listener = mCore->mConsumerListener;
+ } // Autolock scope
+
+ // Call back without lock held
+ if (listener != NULL) {
+ listener->onBuffersReleased();
+ }
+
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueueProducer::waitForFreeSlotThenRelock(const char* caller,
+ bool async, int* found, status_t* returnFlags) const {
+ bool tryAgain = true;
+ while (tryAgain) {
+ if (mCore->mIsAbandoned) {
+ ALOGE("%s: GonkBufferQueue has been abandoned", caller);
+ return NO_INIT;
+ }
+
+ const int maxBufferCount = mCore->getMaxBufferCountLocked(async);
+ if (async && mCore->mOverrideMaxBufferCount) {
+ // FIXME: Some drivers are manually setting the buffer count
+ // (which they shouldn't), so we do this extra test here to
+ // handle that case. This is TEMPORARY until we get this fixed.
+ if (mCore->mOverrideMaxBufferCount < maxBufferCount) {
+ ALOGE("%s: async mode is invalid with buffer count override",
+ caller);
+ return BAD_VALUE;
+ }
+ }
+
+ // Free up any buffers that are in slots beyond the max buffer count
+ //for (int s = maxBufferCount; s < GonkBufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
+ // assert(mSlots[s].mBufferState == GonkBufferSlot::FREE);
+ // if (mSlots[s].mGraphicBuffer != NULL) {
+ // mCore->freeBufferLocked(s);
+ // *returnFlags |= RELEASE_ALL_BUFFERS;
+ // }
+ //}
+
+ // Look for a free buffer to give to the client
+ *found = GonkBufferQueueCore::INVALID_BUFFER_SLOT;
+ int dequeuedCount = 0;
+ int acquiredCount = 0;
+ for (int s = 0; s < maxBufferCount; ++s) {
+ switch (mSlots[s].mBufferState) {
+ case GonkBufferSlot::DEQUEUED:
+ ++dequeuedCount;
+ break;
+ case GonkBufferSlot::ACQUIRED:
+ ++acquiredCount;
+ break;
+ case GonkBufferSlot::FREE:
+ // We return the oldest of the free buffers to avoid
+ // stalling the producer if possible, since the consumer
+ // may still have pending reads of in-flight buffers
+ if (*found == GonkBufferQueueCore::INVALID_BUFFER_SLOT ||
+ mSlots[s].mFrameNumber < mSlots[*found].mFrameNumber) {
+ *found = s;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Producers are not allowed to dequeue more than one buffer if they
+ // did not set a buffer count
+ if (!mCore->mOverrideMaxBufferCount && dequeuedCount) {
+ ALOGE("%s: can't dequeue multiple buffers without setting the "
+ "buffer count", caller);
+ return INVALID_OPERATION;
+ }
+
+ // See whether a buffer has been queued since the last
+ // setBufferCount so we know whether to perform the min undequeued
+ // buffers check below
+ if (mCore->mBufferHasBeenQueued) {
+ // Make sure the producer is not trying to dequeue more buffers
+ // than allowed
+ const int newUndequeuedCount =
+ maxBufferCount - (dequeuedCount + 1);
+ const int minUndequeuedCount =
+ mCore->getMinUndequeuedBufferCountLocked(async);
+ if (newUndequeuedCount < minUndequeuedCount) {
+ ALOGE("%s: min undequeued buffer count (%d) exceeded "
+ "(dequeued=%d undequeued=%d)",
+ caller, minUndequeuedCount,
+ dequeuedCount, newUndequeuedCount);
+ return INVALID_OPERATION;
+ }
+ }
+
+ // If we disconnect and reconnect quickly, we can be in a state where
+ // our slots are empty but we have many buffers in the queue. This can
+ // cause us to run out of memory if we outrun the consumer. Wait here if
+ // it looks like we have too many buffers queued up.
+ bool tooManyBuffers = mCore->mQueue.size()
+ > static_cast<size_t>(maxBufferCount);
+ if (tooManyBuffers) {
+ ALOGV("%s: queue size is %zu, waiting", caller,
+ mCore->mQueue.size());
+ }
+
+ // If no buffer is found, or if the queue has too many buffers
+ // outstanding, wait for a buffer to be acquired or released, or for the
+ // max buffer count to change.
+ tryAgain = (*found == GonkBufferQueueCore::INVALID_BUFFER_SLOT) ||
+ tooManyBuffers;
+ if (tryAgain) {
+ // Return an error if we're in non-blocking mode (producer and
+ // consumer are controlled by the application).
+ // However, the consumer is allowed to briefly acquire an extra
+ // buffer (which could cause us to have to wait here), which is
+ // okay, since it is only used to implement an atomic acquire +
+ // release (e.g., in GLConsumer::updateTexImage())
+ if (mCore->mDequeueBufferCannotBlock &&
+ (acquiredCount <= mCore->mMaxAcquiredBufferCount)) {
+ return WOULD_BLOCK;
+ }
+ mCore->mDequeueCondition.wait(mCore->mMutex);
+ }
+ } // while (tryAgain)
+
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueueProducer::dequeueBuffer(int *outSlot,
+ sp<android::Fence> *outFence, bool async,
+ uint32_t width, uint32_t height, uint32_t format, uint32_t usage) {
+ ATRACE_CALL();
+ { // Autolock scope
+ Mutex::Autolock lock(mCore->mMutex);
+ mConsumerName = mCore->mConsumerName;
+ } // Autolock scope
+
+ ALOGV("dequeueBuffer: async=%s w=%u h=%u format=%#x, usage=%#x",
+ async ? "true" : "false", width, height, format, usage);
+
+ if ((width && !height) || (!width && height)) {
+ ALOGE("dequeueBuffer: invalid size: w=%u h=%u", width, height);
+ return BAD_VALUE;
+ }
+
+ status_t returnFlags = NO_ERROR;
+ // Reset slot
+ *outSlot = GonkBufferQueueCore::INVALID_BUFFER_SLOT;
+
+ bool attachedByConsumer = false;
+
+ { // Autolock scope
+ Mutex::Autolock lock(mCore->mMutex);
+ mCore->waitWhileAllocatingLocked();
+
+ if (format == 0) {
+ format = mCore->mDefaultBufferFormat;
+ }
+
+ // Enable the usage bits the consumer requested
+ usage |= mCore->mConsumerUsageBits;
+
+ int found;
+ status_t status = waitForFreeSlotThenRelock("dequeueBuffer", async,
+ &found, &returnFlags);
+ if (status != NO_ERROR) {
+ return status;
+ }
+
+ // This should not happen
+ if (found == GonkBufferQueueCore::INVALID_BUFFER_SLOT) {
+ ALOGE("dequeueBuffer: no available buffer slots");
+ return -EBUSY;
+ }
+
+ *outSlot = found;
+
+ attachedByConsumer = mSlots[found].mAttachedByConsumer;
+
+ const bool useDefaultSize = !width && !height;
+ if (useDefaultSize) {
+ width = mCore->mDefaultWidth;
+ height = mCore->mDefaultHeight;
+ }
+
+ mSlots[found].mBufferState = GonkBufferSlot::DEQUEUED;
+
+ const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
+ if ((buffer == NULL) ||
+ (static_cast<uint32_t>(buffer->width) != width) ||
+ (static_cast<uint32_t>(buffer->height) != height) ||
+ (static_cast<uint32_t>(buffer->format) != format) ||
+ ((static_cast<uint32_t>(buffer->usage) & usage) != usage))
+ {
+ mSlots[found].mAcquireCalled = false;
+ mSlots[found].mGraphicBuffer = NULL;
+ mSlots[found].mRequestBufferCalled = false;
+ mSlots[found].mFence = Fence::NO_FENCE;
+
+ if (mSlots[found].mTextureClient) {
+ mSlots[found].mTextureClient->ClearRecycleCallback();
+ // release TextureClient in ImageBridge thread
+ RefPtr<TextureClientReleaseTask> task =
+ MakeAndAddRef<TextureClientReleaseTask>(mSlots[found].mTextureClient);
+ mSlots[found].mTextureClient = NULL;
+ ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(task.forget());
+ }
+
+ returnFlags |= BUFFER_NEEDS_REALLOCATION;
+ }
+
+ if (CC_UNLIKELY(mSlots[found].mFence == NULL)) {
+ ALOGE("dequeueBuffer: about to return a NULL fence - "
+ "slot=%d w=%d h=%d format=%u",
+ found, buffer->width, buffer->height, buffer->format);
+ }
+
+ *outFence = mSlots[found].mFence;
+ mSlots[found].mFence = Fence::NO_FENCE;
+ } // Autolock scope
+
+ if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
+ RefPtr<LayersIPCChannel> allocator = ImageBridgeChild::GetSingleton();
+ usage |= GraphicBuffer::USAGE_HW_TEXTURE;
+ GrallocTextureData* texData = GrallocTextureData::Create(IntSize(width,height), format,
+ gfx::BackendType::NONE,
+ usage, allocator);
+ if (!texData) {
+ ALOGE("dequeueBuffer: failed to alloc gralloc buffer");
+ return -ENOMEM;
+ }
+ RefPtr<TextureClient> textureClient = TextureClient::CreateWithData(
+ texData, TextureFlags::RECYCLE | TextureFlags::DEALLOCATE_CLIENT, allocator);
+
+ sp<GraphicBuffer> graphicBuffer = texData->GetGraphicBuffer();
+
+ { // Autolock scope
+ Mutex::Autolock lock(mCore->mMutex);
+
+ if (mCore->mIsAbandoned) {
+ ALOGE("dequeueBuffer: GonkBufferQueue has been abandoned");
+ return NO_INIT;
+ }
+
+ mSlots[*outSlot].mFrameNumber = UINT32_MAX;
+ mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
+ mSlots[*outSlot].mTextureClient = textureClient;
+ } // Autolock scope
+ }
+
+ if (attachedByConsumer) {
+ returnFlags |= BUFFER_NEEDS_REALLOCATION;
+ }
+
+ ALOGV("dequeueBuffer: returning slot=%d/%" PRIu64 " buf=%p flags=%#x",
+ *outSlot,
+ mSlots[*outSlot].mFrameNumber,
+ mSlots[*outSlot].mGraphicBuffer->handle, returnFlags);
+
+ return returnFlags;
+}
+
+status_t GonkBufferQueueProducer::detachBuffer(int slot) {
+ ATRACE_CALL();
+ ATRACE_BUFFER_INDEX(slot);
+ ALOGV("detachBuffer(P): slot %d", slot);
+ Mutex::Autolock lock(mCore->mMutex);
+
+ if (mCore->mIsAbandoned) {
+ ALOGE("detachBuffer(P): GonkBufferQueue has been abandoned");
+ return NO_INIT;
+ }
+
+ if (slot < 0 || slot >= GonkBufferQueueDefs::NUM_BUFFER_SLOTS) {
+ ALOGE("detachBuffer(P): slot index %d out of range [0, %d)",
+ slot, GonkBufferQueueDefs::NUM_BUFFER_SLOTS);
+ return BAD_VALUE;
+ } else if (mSlots[slot].mBufferState != GonkBufferSlot::DEQUEUED) {
+ ALOGE("detachBuffer(P): slot %d is not owned by the producer "
+ "(state = %d)", slot, mSlots[slot].mBufferState);
+ return BAD_VALUE;
+ } else if (!mSlots[slot].mRequestBufferCalled) {
+ ALOGE("detachBuffer(P): buffer in slot %d has not been requested",
+ slot);
+ return BAD_VALUE;
+ }
+
+ mCore->freeBufferLocked(slot);
+ mCore->mDequeueCondition.broadcast();
+
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueueProducer::detachNextBuffer(sp<GraphicBuffer>* outBuffer,
+ sp<Fence>* outFence) {
+ ATRACE_CALL();
+
+ if (outBuffer == NULL) {
+ ALOGE("detachNextBuffer: outBuffer must not be NULL");
+ return BAD_VALUE;
+ } else if (outFence == NULL) {
+ ALOGE("detachNextBuffer: outFence must not be NULL");
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mCore->mMutex);
+ mCore->waitWhileAllocatingLocked();
+
+ if (mCore->mIsAbandoned) {
+ ALOGE("detachNextBuffer: GonkBufferQueue has been abandoned");
+ return NO_INIT;
+ }
+
+ // Find the oldest valid slot
+ int found = GonkBufferQueueCore::INVALID_BUFFER_SLOT;
+ for (int s = 0; s < GonkBufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
+ if (mSlots[s].mBufferState == GonkBufferSlot::FREE &&
+ mSlots[s].mGraphicBuffer != NULL) {
+ if (found == GonkBufferQueueCore::INVALID_BUFFER_SLOT ||
+ mSlots[s].mFrameNumber < mSlots[found].mFrameNumber) {
+ found = s;
+ }
+ }
+ }
+
+ if (found == GonkBufferQueueCore::INVALID_BUFFER_SLOT) {
+ return NO_MEMORY;
+ }
+
+ ALOGV("detachNextBuffer detached slot %d", found);
+
+ *outBuffer = mSlots[found].mGraphicBuffer;
+ *outFence = mSlots[found].mFence;
+ mCore->freeBufferLocked(found);
+
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueueProducer::attachBuffer(int* outSlot,
+ const sp<android::GraphicBuffer>& buffer) {
+ ATRACE_CALL();
+
+ if (outSlot == NULL) {
+ ALOGE("attachBuffer(P): outSlot must not be NULL");
+ return BAD_VALUE;
+ } else if (buffer == NULL) {
+ ALOGE("attachBuffer(P): cannot attach NULL buffer");
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mCore->mMutex);
+ mCore->waitWhileAllocatingLocked();
+
+ status_t returnFlags = NO_ERROR;
+ int found;
+ // TODO: Should we provide an async flag to attachBuffer? It seems
+ // unlikely that buffers which we are attaching to a GonkBufferQueue will
+ // be asynchronous (droppable), but it may not be impossible.
+ status_t status = waitForFreeSlotThenRelock("attachBuffer(P)", false,
+ &found, &returnFlags);
+ if (status != NO_ERROR) {
+ return status;
+ }
+
+ // This should not happen
+ if (found == GonkBufferQueueCore::INVALID_BUFFER_SLOT) {
+ ALOGE("attachBuffer(P): no available buffer slots");
+ return -EBUSY;
+ }
+
+ *outSlot = found;
+ ATRACE_BUFFER_INDEX(*outSlot);
+ ALOGV("attachBuffer(P): returning slot %d flags=%#x",
+ *outSlot, returnFlags);
+
+ mSlots[*outSlot].mGraphicBuffer = buffer;
+ mSlots[*outSlot].mBufferState = GonkBufferSlot::DEQUEUED;
+ mSlots[*outSlot].mFence = Fence::NO_FENCE;
+ mSlots[*outSlot].mRequestBufferCalled = true;
+
+ return returnFlags;
+}
+
+status_t GonkBufferQueueProducer::setSynchronousMode(bool enabled) {
+ ALOGV("setSynchronousMode: enabled=%d", enabled);
+ Mutex::Autolock lock(mCore->mMutex);
+
+ if (mCore->mIsAbandoned) {
+ ALOGE("setSynchronousMode: BufferQueue has been abandoned!");
+ return NO_INIT;
+ }
+
+ if (mSynchronousMode != enabled) {
+ mSynchronousMode = enabled;
+ mCore->mDequeueCondition.broadcast();
+ }
+ return OK;
+}
+
+status_t GonkBufferQueueProducer::queueBuffer(int slot,
+ const QueueBufferInput &input, QueueBufferOutput *output) {
+ ATRACE_CALL();
+
+ int64_t timestamp;
+ bool isAutoTimestamp;
+ Rect crop;
+ int scalingMode;
+ uint32_t transform;
+ uint32_t stickyTransform;
+ bool async;
+ sp<Fence> fence;
+ input.deflate(&timestamp, &isAutoTimestamp, &crop, &scalingMode, &transform,
+ &async, &fence, &stickyTransform);
+
+ if (fence == NULL) {
+ ALOGE("queueBuffer: fence is NULL");
+ // Temporary workaround for b/17946343: soldier-on instead of returning an error. This
+ // prevents the client from dying, at the risk of visible corruption due to hwcomposer
+ // reading the buffer before the producer is done rendering it. Unless the buffer is the
+ // last frame of an animation, the corruption will be transient.
+ fence = Fence::NO_FENCE;
+ // return BAD_VALUE;
+ }
+
+ switch (scalingMode) {
+ case NATIVE_WINDOW_SCALING_MODE_FREEZE:
+ case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
+ case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP:
+ case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP:
+ break;
+ default:
+ ALOGE("queueBuffer: unknown scaling mode %d", scalingMode);
+ return BAD_VALUE;
+ }
+
+ GonkBufferItem item;
+ sp<IConsumerListener> listener;
+ { // Autolock scope
+ Mutex::Autolock lock(mCore->mMutex);
+
+ if (mCore->mIsAbandoned) {
+ ALOGE("queueBuffer: GonkBufferQueue has been abandoned");
+ return NO_INIT;
+ }
+
+ const int maxBufferCount = mCore->getMaxBufferCountLocked(async);
+ if (async && mCore->mOverrideMaxBufferCount) {
+ // FIXME: Some drivers are manually setting the buffer count
+ // (which they shouldn't), so we do this extra test here to
+ // handle that case. This is TEMPORARY until we get this fixed.
+ if (mCore->mOverrideMaxBufferCount < maxBufferCount) {
+ ALOGE("queueBuffer: async mode is invalid with "
+ "buffer count override");
+ return BAD_VALUE;
+ }
+ }
+
+ if (slot < 0 || slot >= maxBufferCount) {
+ ALOGE("queueBuffer: slot index %d out of range [0, %d)",
+ slot, maxBufferCount);
+ return BAD_VALUE;
+ } else if (mSlots[slot].mBufferState != GonkBufferSlot::DEQUEUED) {
+ ALOGE("queueBuffer: slot %d is not owned by the producer "
+ "(state = %d)", slot, mSlots[slot].mBufferState);
+ return BAD_VALUE;
+ } else if (!mSlots[slot].mRequestBufferCalled) {
+ ALOGE("queueBuffer: slot %d was queued without requesting "
+ "a buffer", slot);
+ return BAD_VALUE;
+ }
+
+ ALOGV("queueBuffer: slot=%d/%" PRIu64 " time=%" PRIu64
+ " crop=[%d,%d,%d,%d] transform=%#x scale=%s",
+ slot, mCore->mFrameCounter + 1, timestamp,
+ crop.left, crop.top, crop.right, crop.bottom,
+ transform, GonkBufferItem::scalingModeName(scalingMode));
+
+ const sp<GraphicBuffer>& graphicBuffer(mSlots[slot].mGraphicBuffer);
+ Rect bufferRect(graphicBuffer->getWidth(), graphicBuffer->getHeight());
+ Rect croppedRect;
+ crop.intersect(bufferRect, &croppedRect);
+ if (croppedRect != crop) {
+ ALOGE("queueBuffer: crop rect is not contained within the "
+ "buffer in slot %d", slot);
+ return BAD_VALUE;
+ }
+
+ mSlots[slot].mFence = fence;
+ mSlots[slot].mBufferState = GonkBufferSlot::QUEUED;
+ ++mCore->mFrameCounter;
+ mSlots[slot].mFrameNumber = mCore->mFrameCounter;
+
+ item.mAcquireCalled = mSlots[slot].mAcquireCalled;
+ item.mGraphicBuffer = mSlots[slot].mGraphicBuffer;
+ item.mCrop = crop;
+ item.mTransform = transform & ~NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
+ item.mTransformToDisplayInverse =
+ bool(transform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY);
+ item.mScalingMode = scalingMode;
+ item.mTimestamp = timestamp;
+ item.mIsAutoTimestamp = isAutoTimestamp;
+ item.mFrameNumber = mCore->mFrameCounter;
+ item.mSlot = slot;
+ item.mFence = fence;
+ item.mIsDroppable = mCore->mDequeueBufferCannotBlock || async;
+
+ mStickyTransform = stickyTransform;
+
+ if (mCore->mQueue.empty()) {
+ // When the queue is empty, we can ignore mDequeueBufferCannotBlock
+ // and simply queue this buffer
+ mCore->mQueue.push_back(item);
+ listener = mCore->mConsumerListener;
+ } else {
+ // When the queue is not empty, we need to look at the front buffer
+ // state to see if we need to replace it
+ GonkBufferQueueCore::Fifo::iterator front(mCore->mQueue.begin());
+ if (front->mIsDroppable || !mSynchronousMode) {
+ // If the front queued buffer is still being tracked, we first
+ // mark it as freed
+ if (mCore->stillTracking(front)) {
+ mSlots[front->mSlot].mBufferState = GonkBufferSlot::FREE;
+ // Reset the frame number of the freed buffer so that it is
+ // the first in line to be dequeued again
+ mSlots[front->mSlot].mFrameNumber = 0;
+ }
+ // Overwrite the droppable buffer with the incoming one
+ *front = item;
+ listener = mCore->mConsumerListener;
+ } else {
+ mCore->mQueue.push_back(item);
+ listener = mCore->mConsumerListener;
+ }
+ }
+
+ mCore->mBufferHasBeenQueued = true;
+ mCore->mDequeueCondition.broadcast();
+
+ output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight,
+ mCore->mTransformHint, mCore->mQueue.size());
+
+ item.mGraphicBuffer.clear();
+ item.mSlot = GonkBufferItem::INVALID_BUFFER_SLOT;
+ } // Autolock scope
+
+ // Call back without lock held
+ if (listener != NULL) {
+#if ANDROID_VERSION == 21
+ listener->onFrameAvailable();
+#else
+ listener->onFrameAvailable(reinterpret_cast<::android::BufferItem&>(item));
+#endif
+ }
+
+ return NO_ERROR;
+}
+
+void GonkBufferQueueProducer::cancelBuffer(int slot, const sp<Fence>& fence) {
+ ATRACE_CALL();
+ ALOGV("cancelBuffer: slot %d", slot);
+ Mutex::Autolock lock(mCore->mMutex);
+
+ if (mCore->mIsAbandoned) {
+ ALOGE("cancelBuffer: GonkBufferQueue has been abandoned");
+ return;
+ }
+
+ if (slot < 0 || slot >= GonkBufferQueueDefs::NUM_BUFFER_SLOTS) {
+ ALOGE("cancelBuffer: slot index %d out of range [0, %d)",
+ slot, GonkBufferQueueDefs::NUM_BUFFER_SLOTS);
+ return;
+ } else if (mSlots[slot].mBufferState != GonkBufferSlot::DEQUEUED) {
+ ALOGE("cancelBuffer: slot %d is not owned by the producer "
+ "(state = %d)", slot, mSlots[slot].mBufferState);
+ return;
+ } else if (fence == NULL) {
+ ALOGE("cancelBuffer: fence is NULL");
+ return;
+ }
+
+ mSlots[slot].mBufferState = GonkBufferSlot::FREE;
+ mSlots[slot].mFrameNumber = 0;
+ mSlots[slot].mFence = fence;
+ mCore->mDequeueCondition.broadcast();
+}
+
+int GonkBufferQueueProducer::query(int what, int *outValue) {
+ ATRACE_CALL();
+ Mutex::Autolock lock(mCore->mMutex);
+
+ if (outValue == NULL) {
+ ALOGE("query: outValue was NULL");
+ return BAD_VALUE;
+ }
+
+ if (mCore->mIsAbandoned) {
+ ALOGE("query: GonkBufferQueue has been abandoned");
+ return NO_INIT;
+ }
+
+ int value;
+ switch (what) {
+ case NATIVE_WINDOW_WIDTH:
+ value = mCore->mDefaultWidth;
+ break;
+ case NATIVE_WINDOW_HEIGHT:
+ value = mCore->mDefaultHeight;
+ break;
+ case NATIVE_WINDOW_FORMAT:
+ value = mCore->mDefaultBufferFormat;
+ break;
+ case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
+ value = mCore->getMinUndequeuedBufferCountLocked(false);
+ break;
+ case NATIVE_WINDOW_STICKY_TRANSFORM:
+ value = static_cast<int>(mStickyTransform);
+ break;
+ case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND:
+ value = (mCore->mQueue.size() > 1);
+ break;
+ case NATIVE_WINDOW_CONSUMER_USAGE_BITS:
+ value = mCore->mConsumerUsageBits;
+ break;
+ default:
+ return BAD_VALUE;
+ }
+
+ ALOGV("query: %d? %d", what, value);
+ *outValue = value;
+ return NO_ERROR;
+}
+
+status_t GonkBufferQueueProducer::connect(const sp<IProducerListener>& listener,
+ int api, bool producerControlledByApp, QueueBufferOutput *output) {
+ ATRACE_CALL();
+ Mutex::Autolock lock(mCore->mMutex);
+ mConsumerName = mCore->mConsumerName;
+ ALOGV("connect(P): api=%d producerControlledByApp=%s", api,
+ producerControlledByApp ? "true" : "false");
+
+ if (mCore->mIsAbandoned) {
+ ALOGE("connect(P): GonkBufferQueue has been abandoned");
+ return NO_INIT;
+ }
+
+ if (mCore->mConsumerListener == NULL) {
+ ALOGE("connect(P): GonkBufferQueue has no consumer");
+ return NO_INIT;
+ }
+
+ if (output == NULL) {
+ ALOGE("connect(P): output was NULL");
+ return BAD_VALUE;
+ }
+
+ if (mCore->mConnectedApi != GonkBufferQueueCore::NO_CONNECTED_API) {
+ ALOGE("connect(P): already connected (cur=%d req=%d)", mCore->mConnectedApi,
+ api);
+ return BAD_VALUE;
+ }
+
+ int status = NO_ERROR;
+ switch (api) {
+ case NATIVE_WINDOW_API_EGL:
+ case NATIVE_WINDOW_API_CPU:
+ case NATIVE_WINDOW_API_MEDIA:
+ case NATIVE_WINDOW_API_CAMERA:
+ mCore->mConnectedApi = api;
+ output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight,
+ mCore->mTransformHint, mCore->mQueue.size());
+
+ // Set up a death notification so that we can disconnect
+ // automatically if the remote producer dies
+ if (listener != NULL &&
+ listener->asBinder()->remoteBinder() != NULL) {
+ status = listener->asBinder()->linkToDeath(
+ static_cast<IBinder::DeathRecipient*>(this));
+ if (status != NO_ERROR) {
+ ALOGE("connect(P): linkToDeath failed: %s (%d)",
+ strerror(-status), status);
+ }
+ }
+ mCore->mConnectedProducerListener = listener;
+ break;
+ default:
+ ALOGE("connect(P): unknown API %d", api);
+ status = BAD_VALUE;
+ break;
+ }
+
+ mCore->mBufferHasBeenQueued = false;
+ mCore->mDequeueBufferCannotBlock =
+ mCore->mConsumerControlledByApp && producerControlledByApp;
+
+ return status;
+}
+
+status_t GonkBufferQueueProducer::disconnect(int api) {
+ ATRACE_CALL();
+ ALOGV("disconnect(P): api %d", api);
+
+ int status = NO_ERROR;
+ sp<IConsumerListener> listener;
+ { // Autolock scope
+ Mutex::Autolock lock(mCore->mMutex);
+ mCore->waitWhileAllocatingLocked();
+
+ if (mCore->mIsAbandoned) {
+ // It's not really an error to disconnect after the surface has
+ // been abandoned; it should just be a no-op.
+ return 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 (mCore->mConnectedApi == api) {
+ mCore->freeAllBuffersLocked();
+ mCore->mConnectedApi = GonkBufferQueueCore::NO_CONNECTED_API;
+ mCore->mSidebandStream.clear();
+ mCore->mDequeueCondition.broadcast();
+ listener = mCore->mConsumerListener;
+ } else {
+ ALOGE("disconnect(P): connected to another API "
+ "(cur=%d req=%d)", mCore->mConnectedApi, api);
+ status = BAD_VALUE;
+ }
+ break;
+ default:
+ ALOGE("disconnect(P): unknown API %d", api);
+ status = BAD_VALUE;
+ break;
+ }
+ } // Autolock scope
+
+ // Call back without lock held
+ if (listener != NULL) {
+ listener->onBuffersReleased();
+ }
+
+ return status;
+}
+
+status_t GonkBufferQueueProducer::setSidebandStream(const sp<NativeHandle>& stream) {
+ return INVALID_OPERATION;
+}
+
+void GonkBufferQueueProducer::allocateBuffers(bool async, uint32_t width,
+ uint32_t height, uint32_t format, uint32_t usage) {
+ ALOGE("allocateBuffers: no op");
+}
+
+void GonkBufferQueueProducer::binderDied(const wp<android::IBinder>& /* who */) {
+ // If we're here, it means that a producer we were connected to died.
+ // We're guaranteed that we are still connected to it because we remove
+ // this callback upon disconnect. It's therefore safe to read mConnectedApi
+ // without synchronization here.
+ int api = mCore->mConnectedApi;
+ disconnect(api);
+}
+
+} // namespace android
diff --git a/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueProducer.h b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueProducer.h
new file mode 100644
index 000000000..a1a22416a
--- /dev/null
+++ b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueProducer.h
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ * Copyright (C) 2014 Mozilla Foundation
+ *
+ * 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 NATIVEWINDOW_GONKBUFFERQUEUEPRODUCER_LL_H
+#define NATIVEWINDOW_GONKBUFFERQUEUEPRODUCER_LL_H
+
+#include "GonkBufferQueueDefs.h"
+#include <gui/IGraphicBufferProducer.h>
+
+namespace android {
+
+class GonkBufferQueueProducer : public BnGraphicBufferProducer,
+ private IBinder::DeathRecipient {
+public:
+ friend class GonkBufferQueue; // Needed to access binderDied
+
+ GonkBufferQueueProducer(const sp<GonkBufferQueueCore>& core);
+ virtual ~GonkBufferQueueProducer();
+
+ // requestBuffer returns the GraphicBuffer for slot N.
+ //
+ // In normal operation, this is called the first time slot N is returned
+ // by dequeueBuffer. It must be called again if dequeueBuffer returns
+ // flags indicating that previously-returned buffers are no longer valid.
+ virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf);
+
+ // setBufferCount updates the number of available buffer slots. If this
+ // method succeeds, buffer slots will be both unallocated and owned by
+ // the GonkBufferQueue object (i.e. they are not owned by the producer or
+ // consumer).
+ //
+ // This will fail if the producer has dequeued any buffers, or if
+ // bufferCount is invalid. bufferCount must generally be a value
+ // between the minimum undequeued buffer count (exclusive) and NUM_BUFFER_SLOTS
+ // (inclusive). It may also be set to zero (the default) to indicate
+ // that the producer does not wish to set a value. The minimum value
+ // can be obtained by calling query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+ // ...).
+ //
+ // This may only be called by the producer. The consumer will be told
+ // to discard buffers through the onBuffersReleased callback.
+ virtual status_t setBufferCount(int bufferCount);
+
+ // dequeueBuffer gets the next buffer slot index for the producer 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 outFence parameter will be updated to hold the fence associated with
+ // the buffer. The contents of the buffer must not be overwritten until the
+ // fence signals. If the fence is Fence::NO_FENCE, the buffer may be
+ // written immediately.
+ //
+ // 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. If width and height are both zero, the
+ // default values specified by setDefaultBufferSize() are used instead.
+ //
+ // The pixel formats are enumerated in graphics.h, e.g.
+ // HAL_PIXEL_FORMAT_RGBA_8888. If the format is 0, the default format
+ // will be used.
+ //
+ // The usage argument specifies gralloc buffer usage flags. The values
+ // are enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER. These
+ // will be merged with the usage flags specified by setConsumerUsageBits.
+ //
+ // The return value may be a negative error value or a non-negative
+ // collection of flags. If the flags are set, the return values are
+ // valid, but additional actions must be performed.
+ //
+ // If IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION is set, the
+ // producer must discard cached GraphicBuffer references for the slot
+ // returned in buf.
+ // If IGraphicBufferProducer::RELEASE_ALL_BUFFERS is set, the producer
+ // must discard cached GraphicBuffer references for all slots.
+ //
+ // In both cases, the producer will need to call requestBuffer to get a
+ // GraphicBuffer handle for the returned slot.
+ virtual status_t dequeueBuffer(int *outSlot, sp<Fence>* outFence, bool async,
+ uint32_t width, uint32_t height, uint32_t format, uint32_t usage);
+
+ // See IGraphicBufferProducer::detachBuffer
+ virtual status_t detachBuffer(int slot);
+
+ // See IGraphicBufferProducer::detachNextBuffer
+ virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer,
+ sp<Fence>* outFence);
+
+ // See IGraphicBufferProducer::attachBuffer
+ virtual status_t attachBuffer(int* outSlot, const sp<GraphicBuffer>& buffer);
+
+ // queueBuffer returns a filled buffer to the GonkBufferQueue.
+ //
+ // Additional data is provided in the QueueBufferInput struct. Notably,
+ // 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 producer-specific and should be documented by the
+ // producer.
+ //
+ // The caller may provide a fence that signals when all rendering
+ // operations have completed. Alternatively, NO_FENCE may be used,
+ // indicating that the buffer is ready immediately.
+ //
+ // Some values are returned in the output struct: the current settings
+ // for default width and height, the current transform hint, and the
+ // number of queued buffers.
+ virtual status_t queueBuffer(int slot,
+ const QueueBufferInput& input, QueueBufferOutput* output);
+
+ // cancelBuffer returns a dequeued buffer to the GonkBufferQueue, but doesn't
+ // queue it for use by the consumer.
+ //
+ // The buffer will not be overwritten until the fence signals. The fence
+ // will usually be the one obtained from dequeueBuffer.
+ virtual void cancelBuffer(int slot, const sp<Fence>& fence);
+
+ // Query native window attributes. The "what" values are enumerated in
+ // window.h (e.g. NATIVE_WINDOW_FORMAT).
+ virtual int query(int what, int* outValue);
+
+ // connect attempts to connect a producer API to the GonkBufferQueue. This
+ // must be called before any other IGraphicBufferProducer methods are
+ // called except for getAllocator. A consumer must already be connected.
+ //
+ // This method will fail if connect was previously called on the
+ // GonkBufferQueue and no corresponding disconnect call was made (i.e. if
+ // it's still connected to a producer).
+ //
+ // APIs are enumerated in window.h (e.g. NATIVE_WINDOW_API_CPU).
+ virtual status_t connect(const sp<IProducerListener>& listener,
+ int api, bool producerControlledByApp, QueueBufferOutput* output);
+
+ // disconnect attempts to disconnect a producer API from the GonkBufferQueue.
+ // Calling this method will cause any subsequent calls to other
+ // IGraphicBufferProducer 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 GonkBufferQueue is not currently
+ // connected to the specified producer API.
+ virtual status_t disconnect(int api);
+
+ // Attaches a sideband buffer stream to the IGraphicBufferProducer.
+ //
+ // A sideband stream is a device-specific mechanism for passing buffers
+ // from the producer to the consumer without using dequeueBuffer/
+ // queueBuffer. If a sideband stream is present, the consumer can choose
+ // whether to acquire buffers from the sideband stream or from the queued
+ // buffers.
+ //
+ // Passing NULL or a different stream handle will detach the previous
+ // handle if any.
+ virtual status_t setSidebandStream(const sp<NativeHandle>& stream);
+
+ // See IGraphicBufferProducer::allocateBuffers
+ virtual void allocateBuffers(bool async, uint32_t width, uint32_t height,
+ uint32_t format, uint32_t usage);
+
+ // setSynchronousMode sets 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 acquired in order. In asynchronous mode,
+ // a queued buffer may be replaced by a subsequently queued buffer.
+ //
+ // The default mode is synchronous.
+ // This should be called only during initialization.
+ virtual status_t setSynchronousMode(bool enabled);
+
+private:
+ // This is required by the IBinder::DeathRecipient interface
+ virtual void binderDied(const wp<IBinder>& who);
+
+ // waitForFreeSlotThenRelock finds the oldest slot in the FREE state. It may
+ // block if there are no available slots and we are not in non-blocking
+ // mode (producer and consumer controlled by the application). If it blocks,
+ // it will release mCore->mMutex while blocked so that other operations on
+ // the GonkBufferQueue may succeed.
+ status_t waitForFreeSlotThenRelock(const char* caller, bool async,
+ int* found, status_t* returnFlags) const;
+
+ sp<GonkBufferQueueCore> mCore;
+
+ // This references mCore->mSlots. Lock mCore->mMutex while accessing.
+ GonkBufferQueueDefs::SlotsType& mSlots;
+
+ // This is a cached copy of the name stored in the GonkBufferQueueCore.
+ // It's updated during connect and dequeueBuffer (which should catch
+ // most updates).
+ String8 mConsumerName;
+
+ // mSynchronousMode whether we're in synchronous mode or not
+ bool mSynchronousMode;
+
+ uint32_t mStickyTransform;
+
+}; // class GonkBufferQueueProducer
+
+} // namespace android
+
+#endif
diff --git a/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferSlot.cpp b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferSlot.cpp
new file mode 100644
index 000000000..9e4a424a9
--- /dev/null
+++ b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferSlot.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ * Copyright (C) 2014 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "GonkBufferSlot.h"
+
+namespace android {
+
+const char* GonkBufferSlot::bufferStateName(BufferState state) {
+ switch (state) {
+ case GonkBufferSlot::DEQUEUED: return "DEQUEUED";
+ case GonkBufferSlot::QUEUED: return "QUEUED";
+ case GonkBufferSlot::FREE: return "FREE";
+ case GonkBufferSlot::ACQUIRED: return "ACQUIRED";
+ default: return "Unknown";
+ }
+}
+
+} // namespace android
diff --git a/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferSlot.h b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferSlot.h
new file mode 100644
index 000000000..759bb7b23
--- /dev/null
+++ b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferSlot.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ * Copyright (C) 2014 Mozilla Foundation
+ *
+ * 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 NATIVEWINDOW_GONKBUFFERSLOT_LL_H
+#define NATIVEWINDOW_GONKBUFFERSLOT_LL_H
+
+#include <ui/Fence.h>
+#include <ui/GraphicBuffer.h>
+
+#include <utils/StrongPointer.h>
+
+#include "mozilla/layers/TextureClient.h"
+
+namespace android {
+
+struct GonkBufferSlot {
+ typedef mozilla::layers::TextureClient TextureClient;
+
+ GonkBufferSlot()
+ : mBufferState(GonkBufferSlot::FREE),
+ mRequestBufferCalled(false),
+ mFrameNumber(0),
+ mAcquireCalled(false),
+ mNeedsCleanupOnRelease(false),
+ mAttachedByConsumer(false) {
+ }
+
+ // mGraphicBuffer points to the buffer allocated for this slot or is NULL
+ // if no buffer has been allocated.
+ sp<GraphicBuffer> mGraphicBuffer;
+
+ // BufferState represents the different states in which a buffer slot
+ // can be. All slots are initially FREE.
+ enum BufferState {
+ // FREE indicates that the buffer is available to be dequeued
+ // by the producer. The buffer may be in use by the consumer for
+ // a finite time, so the buffer must not be modified until the
+ // associated fence is signaled.
+ //
+ // The slot is "owned" by BufferQueue. It transitions to DEQUEUED
+ // when dequeueBuffer is called.
+ FREE = 0,
+
+ // DEQUEUED indicates that the buffer has been dequeued by the
+ // producer, but has not yet been queued or canceled. The
+ // producer may modify the buffer's contents as soon as the
+ // associated ready fence is signaled.
+ //
+ // The slot is "owned" by the producer. It can transition to
+ // QUEUED (via queueBuffer) or back to FREE (via cancelBuffer).
+ DEQUEUED = 1,
+
+ // QUEUED indicates that the buffer has been filled by the
+ // producer and queued for use by the consumer. The buffer
+ // contents may continue to be modified for a finite time, so
+ // the contents must not be accessed until the associated fence
+ // is signaled.
+ //
+ // The slot is "owned" by BufferQueue. It can transition to
+ // ACQUIRED (via acquireBuffer) or to FREE (if another buffer is
+ // queued in asynchronous mode).
+ QUEUED = 2,
+
+ // ACQUIRED indicates that the buffer has been acquired by the
+ // consumer. As with QUEUED, the contents must not be accessed
+ // by the consumer until the fence is signaled.
+ //
+ // The slot is "owned" by the consumer. It transitions to FREE
+ // when releaseBuffer is called.
+ ACQUIRED = 3
+ };
+
+ static const char* bufferStateName(BufferState state);
+
+ // mBufferState is the current state of this buffer slot.
+ BufferState mBufferState;
+
+ // mRequestBufferCalled is used for validating that the producer did
+ // call requestBuffer() when told to do so. Technically this is not
+ // needed but useful for debugging and catching producer bugs.
+ bool mRequestBufferCalled;
+
+ // mFrameNumber is the number of the queued frame for this slot. This
+ // is used to dequeue buffers in LRU order (useful because buffers
+ // may be released before their release fence is signaled).
+ uint64_t mFrameNumber;
+
+ // mFence is a fence which will signal when work initiated by the
+ // previous owner of the buffer is finished. When the buffer is FREE,
+ // the fence indicates when the consumer has finished reading
+ // from the buffer, or when the producer has finished writing if it
+ // called cancelBuffer after queueing some writes. When the buffer is
+ // QUEUED, it indicates when the producer has finished filling the
+ // buffer. When the buffer is DEQUEUED or ACQUIRED, the fence has been
+ // passed to the consumer or producer along with ownership of the
+ // buffer, and mFence is set to NO_FENCE.
+ sp<Fence> mFence;
+
+ // Indicates whether this buffer has been seen by a consumer yet
+ bool mAcquireCalled;
+
+ // Indicates whether this buffer needs to be cleaned up by the
+ // consumer. This is set when a buffer in ACQUIRED state is freed.
+ // It causes releaseBuffer to return STALE_BUFFER_SLOT.
+ bool mNeedsCleanupOnRelease;
+
+ // Indicates whether the buffer was attached on the consumer side.
+ // If so, it needs to set the BUFFER_NEEDS_REALLOCATION flag when dequeued
+ // to prevent the producer from using a stale cached buffer.
+ bool mAttachedByConsumer;
+
+ // mTextureClient is a thin abstraction over remotely allocated GraphicBuffer.
+ RefPtr<TextureClient> mTextureClient;
+};
+
+} // namespace android
+
+#endif
diff --git a/widget/gonk/nativewindow/GonkConsumerBaseJB.cpp b/widget/gonk/nativewindow/GonkConsumerBaseJB.cpp
new file mode 100644
index 000000000..1ee37e4e2
--- /dev/null
+++ b/widget/gonk/nativewindow/GonkConsumerBaseJB.cpp
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2013 Mozilla Foundation
+ *
+ * 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 "GonkConsumerBase"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+//#define LOG_NDEBUG 0
+
+#define EGL_EGLEXT_PROTOTYPES
+
+#include <hardware/hardware.h>
+
+#include <gui/IGraphicBufferAlloc.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+#include "GonkConsumerBaseJB.h"
+
+// Macros for including the GonkConsumerBase name in log messages
+#define CB_LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
+#define CB_LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
+#define CB_LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
+#define CB_LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
+#define CB_LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __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);
+}
+
+GonkConsumerBase::GonkConsumerBase(const sp<GonkBufferQueue>& bufferQueue) :
+ mAbandoned(false),
+ 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<GonkBufferQueue::ConsumerListener> listener;
+ sp<GonkBufferQueue::ConsumerListener> proxy;
+ listener = static_cast<GonkBufferQueue::ConsumerListener*>(this);
+ proxy = new GonkBufferQueue::ProxyConsumerListener(listener);
+
+ status_t err = mBufferQueue->consumerConnect(proxy);
+ if (err != NO_ERROR) {
+ CB_LOGE("GonkConsumerBase: error connecting to GonkBufferQueue: %s (%d)",
+ strerror(-err), err);
+ } else {
+ mBufferQueue->setConsumerName(mName);
+ }
+}
+
+GonkConsumerBase::~GonkConsumerBase() {
+ CB_LOGV("~GonkConsumerBase");
+ Mutex::Autolock lock(mMutex);
+
+ // Verify that abandon() has been called before we get here. This should
+ // be done by GonkConsumerBase::onLastStrongRef(), but it's possible for a
+ // derived class to override that method and not call
+ // GonkConsumerBase::onLastStrongRef().
+ LOG_ALWAYS_FATAL_IF(!mAbandoned, "[%s] ~GonkConsumerBase was called, but the "
+ "consumer is not abandoned!", mName.string());
+}
+
+void GonkConsumerBase::onLastStrongRef(const void* id) {
+ abandon();
+}
+
+void GonkConsumerBase::freeBufferLocked(int slotIndex) {
+ CB_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
+ mSlots[slotIndex].mGraphicBuffer = 0;
+ mSlots[slotIndex].mFence = Fence::NO_FENCE;
+}
+
+// Used for refactoring, should not be in final interface
+sp<GonkBufferQueue> GonkConsumerBase::getBufferQueue() const {
+ Mutex::Autolock lock(mMutex);
+ return mBufferQueue;
+}
+
+void GonkConsumerBase::onFrameAvailable() {
+ CB_LOGV("onFrameAvailable");
+
+ sp<FrameAvailableListener> listener;
+ { // scope for the lock
+ Mutex::Autolock lock(mMutex);
+#if ANDROID_VERSION == 17
+ listener = mFrameAvailableListener;
+#else
+ listener = mFrameAvailableListener.promote();
+#endif
+ }
+
+ if (listener != NULL) {
+ CB_LOGV("actually calling onFrameAvailable");
+ listener->onFrameAvailable();
+ }
+}
+
+void GonkConsumerBase::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 < GonkBufferQueue::NUM_BUFFER_SLOTS; i++) {
+ if (mask & (1 << i)) {
+ freeBufferLocked(i);
+ }
+ }
+}
+
+void GonkConsumerBase::abandon() {
+ CB_LOGV("abandon");
+ Mutex::Autolock lock(mMutex);
+
+ if (!mAbandoned) {
+ abandonLocked();
+ mAbandoned = true;
+ }
+}
+
+void GonkConsumerBase::abandonLocked() {
+ CB_LOGV("abandonLocked");
+ for (int i =0; i < GonkBufferQueue::NUM_BUFFER_SLOTS; i++) {
+ freeBufferLocked(i);
+ }
+ // disconnect from the GonkBufferQueue
+ mBufferQueue->consumerDisconnect();
+ mBufferQueue.clear();
+}
+
+void GonkConsumerBase::setFrameAvailableListener(
+#if ANDROID_VERSION == 17
+ const sp<FrameAvailableListener>& listener) {
+#else
+ const wp<FrameAvailableListener>& listener) {
+#endif
+ CB_LOGV("setFrameAvailableListener");
+ Mutex::Autolock lock(mMutex);
+ mFrameAvailableListener = listener;
+}
+
+void GonkConsumerBase::dump(String8& result) const {
+ char buffer[1024];
+ dump(result, "", buffer, 1024);
+}
+
+void GonkConsumerBase::dump(String8& result, const char* prefix,
+ char* buffer, size_t size) const {
+ Mutex::Autolock _l(mMutex);
+ dumpLocked(result, prefix, buffer, size);
+}
+
+void GonkConsumerBase::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->dumpToString(result, prefix, buffer, SIZE);
+ }
+}
+
+status_t GonkConsumerBase::acquireBufferLocked(GonkBufferQueue::BufferItem *item) {
+ status_t err = mBufferQueue->acquireBuffer(item);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ if (item->mGraphicBuffer != NULL) {
+ mSlots[item->mBuf].mGraphicBuffer = item->mGraphicBuffer;
+ }
+
+ mSlots[item->mBuf].mFence = item->mFence;
+
+ CB_LOGV("acquireBufferLocked: -> slot=%d", item->mBuf);
+
+ return OK;
+}
+
+status_t GonkConsumerBase::addReleaseFence(int slot, const sp<Fence>& fence) {
+ Mutex::Autolock lock(mMutex);
+ return addReleaseFenceLocked(slot, fence);
+}
+
+status_t GonkConsumerBase::addReleaseFenceLocked(int slot, const sp<Fence>& fence) {
+ CB_LOGV("addReleaseFenceLocked: slot=%d", slot);
+
+ if (!mSlots[slot].mFence.get()) {
+ mSlots[slot].mFence = fence;
+ } else {
+ sp<Fence> mergedFence = Fence::merge(
+ String8::format("%.28s:%d", mName.string(), slot),
+ mSlots[slot].mFence, fence);
+ if (!mergedFence.get()) {
+ CB_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
+ mSlots[slot].mFence = fence;
+ return BAD_VALUE;
+ }
+ mSlots[slot].mFence = mergedFence;
+ }
+
+ return OK;
+}
+
+status_t GonkConsumerBase::releaseBufferLocked(int slot) {
+ CB_LOGV("releaseBufferLocked: slot=%d", slot);
+ status_t err = mBufferQueue->releaseBuffer(slot, mSlots[slot].mFence);
+ if (err == GonkBufferQueue::STALE_BUFFER_SLOT) {
+ freeBufferLocked(slot);
+ }
+
+ mSlots[slot].mFence = Fence::NO_FENCE;
+
+ return err;
+}
+
+} // namespace android
diff --git a/widget/gonk/nativewindow/GonkConsumerBaseJB.h b/widget/gonk/nativewindow/GonkConsumerBaseJB.h
new file mode 100644
index 000000000..8f523af37
--- /dev/null
+++ b/widget/gonk/nativewindow/GonkConsumerBaseJB.h
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2013 Mozilla Foundation
+ *
+ * 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 NATIVEWINDOW_GONKCONSUMERBASE_JB_H
+#define NATIVEWINDOW_GONKCONSUMERBASE_JB_H
+
+#include <ui/GraphicBuffer.h>
+
+#include <utils/String8.h>
+#include <utils/Vector.h>
+#include <utils/threads.h>
+
+#include "GonkBufferQueueJB.h"
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+class String8;
+
+// GonkConsumerBase is a base class for GonkBufferQueue consumer end-points. It
+// handles common tasks like management of the connection to the GonkBufferQueue
+// and the buffer pool.
+class GonkConsumerBase : public virtual RefBase,
+ protected GonkBufferQueue::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 ~GonkConsumerBase();
+
+ // abandon frees all the buffers and puts the GonkConsumerBase into the
+ // 'abandoned' state. Once put in this state the GonkConsumerBase can never
+ // leave it. When in the 'abandoned' state, all methods of the
+ // IGraphicBufferProducer 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 GonkConsumerBase, 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 GonkConsumerBase that will be used to identify it in
+ // log messages.
+ void setName(const String8& name);
+
+ // getBufferQueue returns the GonkBufferQueue object to which this
+ // GonkConsumerBase is connected.
+ sp<GonkBufferQueue> getBufferQueue() const;
+
+ // dump writes the current state to a string. Child classes should add
+ // their state to the dump by overriding 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.
+#if ANDROID_VERSION == 17
+ void setFrameAvailableListener(const sp<FrameAvailableListener>& listener);
+#else
+ void setFrameAvailableListener(const wp<FrameAvailableListener>& listener);
+#endif
+
+private:
+ GonkConsumerBase(const GonkConsumerBase&);
+ void operator=(const GonkConsumerBase&);
+
+protected:
+
+ // GonkConsumerBase constructs a new GonkConsumerBase object to consume image
+ // buffers from the given GonkBufferQueue.
+ GonkConsumerBase(const sp<GonkBufferQueue> &bufferQueue);
+
+ // onLastStrongRef gets called by RefBase just before the dtor of the most
+ // derived class. It is used to clean up the buffers so that GonkConsumerBase
+ // can coordinate the clean-up by calling into virtual methods implemented
+ // by the derived classes. This would not be possible from the
+ // ConsuemrBase dtor because by the time that gets called the derived
+ // classes have already been destructed.
+ //
+ // This methods should not need to be overridden by derived classes, but
+ // if they are overridden the GonkConsumerBase implementation must be called
+ // from the derived class.
+ virtual void onLastStrongRef(const void* id);
+
+ // Implementation of the GonkBufferQueue::ConsumerListener interface. These
+ // calls are used to notify the GonkConsumerBase of asynchronous events in the
+ // GonkBufferQueue. These methods should not need to be overridden by derived
+ // classes, but if they are overridden the GonkConsumerBase implementation
+ // must be called from the derived class.
+ 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. Otherwise it has no effect.
+ //
+ // Derived classes should override this method to clean up any state they
+ // keep per slot. If it is overridden, the derived class's implementation
+ // must call GonkConsumerBase::freeBufferLocked.
+ //
+ // This method must be called with mMutex locked.
+ virtual void freeBufferLocked(int slotIndex);
+
+ // abandonLocked puts the GonkBufferQueue 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.
+ //
+ // Derived classes should override this method to clean up any object
+ // state they keep (as opposed to per-slot state). If it is overridden,
+ // the derived class's implementation must call GonkConsumerBase::abandonLocked.
+ //
+ // This method must be called with mMutex locked.
+ virtual void abandonLocked();
+
+ // dumpLocked dumps the current state of the GonkConsumerBase object to the
+ // result string. Each line is prefixed with the string pointed to by the
+ // prefix argument. The buffer argument points to a buffer that may be
+ // used for intermediate formatting data, and the size of that buffer is
+ // indicated by the size argument.
+ //
+ // Derived classes should override this method to dump their internal
+ // state. If this method is overridden the derived class's implementation
+ // should call GonkConsumerBase::dumpLocked.
+ //
+ // This method must be called with mMutex locked.
+ virtual void dumpLocked(String8& result, const char* prefix, char* buffer,
+ size_t size) const;
+
+ // acquireBufferLocked fetches the next buffer from the GonkBufferQueue and
+ // updates the buffer slot for the buffer returned.
+ //
+ // Derived classes should override this method to perform any
+ // initialization that must take place the first time a buffer is assigned
+ // to a slot. If it is overridden the derived class's implementation must
+ // call GonkConsumerBase::acquireBufferLocked.
+ virtual status_t acquireBufferLocked(GonkBufferQueue::BufferItem *item);
+
+ // releaseBufferLocked relinquishes control over a buffer, returning that
+ // control to the GonkBufferQueue.
+ //
+ // Derived classes should override this method to perform any cleanup that
+ // must take place when a buffer is released back to the GonkBufferQueue. If
+ // it is overridden the derived class's implementation must call
+ // GonkConsumerBase::releaseBufferLocked.
+ virtual status_t releaseBufferLocked(int buf);
+
+ // addReleaseFence* adds the sync points associated with a fence to the set
+ // of sync points that must be reached before the buffer in the given slot
+ // may be used after the slot has been released. This should be called by
+ // derived classes each time some asynchronous work is kicked off that
+ // references the buffer.
+ status_t addReleaseFence(int slot, const sp<Fence>& fence);
+ status_t addReleaseFenceLocked(int slot, const sp<Fence>& fence);
+
+ // Slot contains the information and object references that
+ // GonkConsumerBase maintains about a GonkBufferQueue buffer slot.
+ struct Slot {
+ // mGraphicBuffer is the Gralloc buffer store in the slot or NULL if
+ // no Gralloc buffer is in the slot.
+ sp<GraphicBuffer> 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<Fence> mFence;
+ };
+
+ // mSlots stores the buffers that have been allocated by the GonkBufferQueue
+ // for each buffer slot. It is initialized to null pointers, and gets
+ // filled in with the result of GonkBufferQueue::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[GonkBufferQueue::NUM_BUFFER_SLOTS];
+
+ // mAbandoned indicates that the GonkBufferQueue will no longer be used to
+ // consume images buffers pushed to it using the IGraphicBufferProducer
+ // interface. It is initialized to false, and set to true in the abandon
+ // method. A GonkBufferQueue that has been abandoned will return the NO_INIT
+ // error from all IGonkConsumerBase methods capable of returning an error.
+ bool mAbandoned;
+
+ // mName is a string used to identify the GonkConsumerBase 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.
+#if ANDROID_VERSION == 17
+ sp<FrameAvailableListener> mFrameAvailableListener;
+#else
+ wp<FrameAvailableListener> mFrameAvailableListener;
+#endif
+
+ // The GonkConsumerBase has-a GonkBufferQueue and is responsible for creating this object
+ // if none is supplied
+ sp<GonkBufferQueue> mBufferQueue;
+
+ // mMutex is the mutex used to prevent concurrent access to the member
+ // variables of GonkConsumerBase objects. It must be locked whenever the
+ // member variables are accessed or when any of the *Locked methods are
+ // called.
+ //
+ // This mutex is intended to be locked by derived classes.
+ mutable Mutex mMutex;
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // NATIVEWINDOW_GONKCONSUMERBASE_H
diff --git a/widget/gonk/nativewindow/GonkConsumerBaseKK.cpp b/widget/gonk/nativewindow/GonkConsumerBaseKK.cpp
new file mode 100644
index 000000000..3fc9fc16c
--- /dev/null
+++ b/widget/gonk/nativewindow/GonkConsumerBaseKK.cpp
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2013 Mozilla Foundation
+ *
+ * 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 "GonkConsumerBase"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+//#define LOG_NDEBUG 0
+
+#define EGL_EGLEXT_PROTOTYPES
+
+#include <hardware/hardware.h>
+
+#include <gui/IGraphicBufferAlloc.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+#include "GonkConsumerBaseKK.h"
+
+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);
+}
+
+GonkConsumerBase::GonkConsumerBase(const sp<GonkBufferQueue>& bufferQueue, bool controlledByApp) :
+ mAbandoned(false),
+ mConsumer(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<ConsumerListener> listener = static_cast<ConsumerListener*>(this);
+ sp<IConsumerListener> proxy = new GonkBufferQueue::ProxyConsumerListener(listener);
+
+ status_t err = mConsumer->consumerConnect(proxy, controlledByApp);
+ if (err != NO_ERROR) {
+ ALOGE("GonkConsumerBase: error connecting to GonkBufferQueue: %s (%d)",
+ strerror(-err), err);
+ } else {
+ mConsumer->setConsumerName(mName);
+ }
+}
+
+GonkConsumerBase::~GonkConsumerBase() {
+ ALOGV("~GonkConsumerBase");
+ Mutex::Autolock lock(mMutex);
+
+ // Verify that abandon() has been called before we get here. This should
+ // be done by GonkConsumerBase::onLastStrongRef(), but it's possible for a
+ // derived class to override that method and not call
+ // GonkConsumerBase::onLastStrongRef().
+ LOG_ALWAYS_FATAL_IF(!mAbandoned, "[%s] ~GonkConsumerBase was called, but the "
+ "consumer is not abandoned!", mName.string());
+}
+
+void GonkConsumerBase::onLastStrongRef(const void* id) {
+ abandon();
+}
+
+void GonkConsumerBase::freeBufferLocked(int slotIndex) {
+ ALOGV("freeBufferLocked: slotIndex=%d", slotIndex);
+ mSlots[slotIndex].mGraphicBuffer = 0;
+ mSlots[slotIndex].mFence = Fence::NO_FENCE;
+ mSlots[slotIndex].mFrameNumber = 0;
+}
+
+// Used for refactoring, should not be in final interface
+sp<GonkBufferQueue> GonkConsumerBase::getBufferQueue() const {
+ Mutex::Autolock lock(mMutex);
+ return mConsumer;
+}
+
+void GonkConsumerBase::onFrameAvailable() {
+ ALOGV("onFrameAvailable");
+
+ sp<FrameAvailableListener> listener;
+ { // scope for the lock
+ Mutex::Autolock lock(mMutex);
+ listener = mFrameAvailableListener.promote();
+ }
+
+ if (listener != NULL) {
+ ALOGV("actually calling onFrameAvailable");
+ listener->onFrameAvailable();
+ }
+}
+
+void GonkConsumerBase::onBuffersReleased() {
+ Mutex::Autolock lock(mMutex);
+
+ ALOGV("onBuffersReleased");
+
+ if (mAbandoned) {
+ // Nothing to do if we're already abandoned.
+ return;
+ }
+
+ uint32_t mask = 0;
+ mConsumer->getReleasedBuffers(&mask);
+ for (int i = 0; i < GonkBufferQueue::NUM_BUFFER_SLOTS; i++) {
+ if (mask & (1 << i)) {
+ freeBufferLocked(i);
+ }
+ }
+}
+
+void GonkConsumerBase::abandon() {
+ ALOGV("abandon");
+ Mutex::Autolock lock(mMutex);
+
+ if (!mAbandoned) {
+ abandonLocked();
+ mAbandoned = true;
+ }
+}
+
+void GonkConsumerBase::abandonLocked() {
+ ALOGV("abandonLocked");
+ for (int i =0; i < GonkBufferQueue::NUM_BUFFER_SLOTS; i++) {
+ freeBufferLocked(i);
+ }
+ // disconnect from the BufferQueue
+ mConsumer->consumerDisconnect();
+ mConsumer.clear();
+}
+
+void GonkConsumerBase::setFrameAvailableListener(
+ const wp<FrameAvailableListener>& listener) {
+ ALOGV("setFrameAvailableListener");
+ Mutex::Autolock lock(mMutex);
+ mFrameAvailableListener = listener;
+}
+
+void GonkConsumerBase::dump(String8& result) const {
+ dump(result, "");
+}
+
+void GonkConsumerBase::dump(String8& result, const char* prefix) const {
+ Mutex::Autolock _l(mMutex);
+ dumpLocked(result, prefix);
+}
+
+void GonkConsumerBase::dumpLocked(String8& result, const char* prefix) const {
+ result.appendFormat("%smAbandoned=%d\n", prefix, int(mAbandoned));
+
+ if (!mAbandoned) {
+ mConsumer->dumpToString(result, prefix);
+ }
+}
+
+status_t GonkConsumerBase::acquireBufferLocked(IGonkGraphicBufferConsumer::BufferItem *item,
+ nsecs_t presentWhen) {
+ status_t err = mConsumer->acquireBuffer(item, presentWhen);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ if (item->mGraphicBuffer != NULL) {
+ mSlots[item->mBuf].mGraphicBuffer = item->mGraphicBuffer;
+ }
+
+ mSlots[item->mBuf].mFrameNumber = item->mFrameNumber;
+ mSlots[item->mBuf].mFence = item->mFence;
+
+ ALOGV("acquireBufferLocked: -> slot=%d", item->mBuf);
+
+ return OK;
+}
+
+status_t GonkConsumerBase::addReleaseFence(int slot,
+ const sp<GraphicBuffer> graphicBuffer, const sp<Fence>& fence) {
+ Mutex::Autolock lock(mMutex);
+ return addReleaseFenceLocked(slot, graphicBuffer, fence);
+}
+
+status_t GonkConsumerBase::addReleaseFenceLocked(int slot,
+ const sp<GraphicBuffer> graphicBuffer, const sp<Fence>& fence) {
+ ALOGV("addReleaseFenceLocked: slot=%d", slot);
+
+ // If consumer no longer tracks this graphicBuffer, we can safely
+ // drop this fence, as it will never be received by the producer.
+ if (!stillTracking(slot, graphicBuffer)) {
+ return OK;
+ }
+
+ if (!mSlots[slot].mFence.get()) {
+ mSlots[slot].mFence = fence;
+ } else {
+ sp<Fence> mergedFence = Fence::merge(
+ String8::format("%.28s:%d", mName.string(), slot),
+ mSlots[slot].mFence, fence);
+ if (!mergedFence.get()) {
+ ALOGE("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
+ mSlots[slot].mFence = fence;
+ return BAD_VALUE;
+ }
+ mSlots[slot].mFence = mergedFence;
+ }
+
+ return OK;
+}
+
+status_t GonkConsumerBase::releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer) {
+ // If consumer no longer tracks this graphicBuffer (we received a new
+ // buffer on the same slot), the buffer producer is definitely no longer
+ // tracking it.
+ if (!stillTracking(slot, graphicBuffer)) {
+ return OK;
+ }
+
+ ALOGV("releaseBufferLocked: slot=%d/%llu",
+ slot, mSlots[slot].mFrameNumber);
+ status_t err = mConsumer->releaseBuffer(slot, mSlots[slot].mFrameNumber, mSlots[slot].mFence);
+ if (err == GonkBufferQueue::STALE_BUFFER_SLOT) {
+ freeBufferLocked(slot);
+ }
+
+ mSlots[slot].mFence = Fence::NO_FENCE;
+
+ return err;
+}
+
+bool GonkConsumerBase::stillTracking(int slot,
+ const sp<GraphicBuffer> graphicBuffer) {
+ if (slot < 0 || slot >= GonkBufferQueue::NUM_BUFFER_SLOTS) {
+ return false;
+ }
+ return (mSlots[slot].mGraphicBuffer != NULL &&
+ mSlots[slot].mGraphicBuffer->handle == graphicBuffer->handle);
+}
+
+} // namespace android
diff --git a/widget/gonk/nativewindow/GonkConsumerBaseKK.h b/widget/gonk/nativewindow/GonkConsumerBaseKK.h
new file mode 100644
index 000000000..e198ad843
--- /dev/null
+++ b/widget/gonk/nativewindow/GonkConsumerBaseKK.h
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2013 Mozilla Foundation
+ *
+ * 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 NATIVEWINDOW_GONKCONSUMERBASE_KK_H
+#define NATIVEWINDOW_GONKCONSUMERBASE_KK_H
+
+#include <ui/GraphicBuffer.h>
+
+#include <utils/String8.h>
+#include <utils/Vector.h>
+#include <utils/threads.h>
+#include <gui/IConsumerListener.h>
+
+#include "GonkBufferQueueKK.h"
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+class String8;
+
+// GonkConsumerBase is a base class for GonkBufferQueue consumer end-points. It
+// handles common tasks like management of the connection to the GonkBufferQueue
+// and the buffer pool.
+class GonkConsumerBase : public virtual RefBase,
+ protected 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 ~GonkConsumerBase();
+
+ // abandon frees all the buffers and puts the GonkConsumerBase into the
+ // 'abandoned' state. Once put in this state the GonkConsumerBase can never
+ // leave it. When in the 'abandoned' state, all methods of the
+ // IGraphicBufferProducer 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 GonkConsumerBase, 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 GonkConsumerBase that will be used to identify it in
+ // log messages.
+ void setName(const String8& name);
+
+ // getBufferQueue returns the GonkBufferQueue object to which this
+ // GonkConsumerBase is connected.
+ sp<GonkBufferQueue> getBufferQueue() const;
+
+ // dump writes the current state to a string. Child classes should add
+ // their state to the dump by overriding 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) const;
+
+ // setFrameAvailableListener sets the listener object that will be notified
+ // when a new frame becomes available.
+ void setFrameAvailableListener(const wp<FrameAvailableListener>& listener);
+
+private:
+ GonkConsumerBase(const GonkConsumerBase&);
+ void operator=(const GonkConsumerBase&);
+
+protected:
+
+ // GonkConsumerBase constructs a new GonkConsumerBase object to consume image
+ // buffers from the given GonkBufferQueue.
+ GonkConsumerBase(const sp<GonkBufferQueue>& bufferQueue, bool controlledByApp = false);
+
+ // onLastStrongRef gets called by RefBase just before the dtor of the most
+ // derived class. It is used to clean up the buffers so that GonkConsumerBase
+ // can coordinate the clean-up by calling into virtual methods implemented
+ // by the derived classes. This would not be possible from the
+ // ConsuemrBase dtor because by the time that gets called the derived
+ // classes have already been destructed.
+ //
+ // This methods should not need to be overridden by derived classes, but
+ // if they are overridden the GonkConsumerBase implementation must be called
+ // from the derived class.
+ virtual void onLastStrongRef(const void* id);
+
+ // Implementation of the GonkBufferQueue::ConsumerListener interface. These
+ // calls are used to notify the GonkConsumerBase of asynchronous events in the
+ // GonkBufferQueue. These methods should not need to be overridden by derived
+ // classes, but if they are overridden the GonkConsumerBase implementation
+ // must be called from the derived class.
+ 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. Otherwise it has no effect.
+ //
+ // Derived classes should override this method to clean up any state they
+ // keep per slot. If it is overridden, the derived class's implementation
+ // must call GonkConsumerBase::freeBufferLocked.
+ //
+ // This method must be called with mMutex locked.
+ virtual void freeBufferLocked(int slotIndex);
+
+ // abandonLocked puts the GonkBufferQueue 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.
+ //
+ // Derived classes should override this method to clean up any object
+ // state they keep (as opposed to per-slot state). If it is overridden,
+ // the derived class's implementation must call GonkConsumerBase::abandonLocked.
+ //
+ // This method must be called with mMutex locked.
+ virtual void abandonLocked();
+
+ // dumpLocked dumps the current state of the GonkConsumerBase object to the
+ // result string. Each line is prefixed with the string pointed to by the
+ // prefix argument. The buffer argument points to a buffer that may be
+ // used for intermediate formatting data, and the size of that buffer is
+ // indicated by the size argument.
+ //
+ // Derived classes should override this method to dump their internal
+ // state. If this method is overridden the derived class's implementation
+ // should call GonkConsumerBase::dumpLocked.
+ //
+ // This method must be called with mMutex locked.
+ virtual void dumpLocked(String8& result, const char* prefix) const;
+
+ // acquireBufferLocked fetches the next buffer from the GonkBufferQueue and
+ // updates the buffer slot for the buffer returned.
+ //
+ // Derived classes should override this method to perform any
+ // initialization that must take place the first time a buffer is assigned
+ // to a slot. If it is overridden the derived class's implementation must
+ // call GonkConsumerBase::acquireBufferLocked.
+ virtual status_t acquireBufferLocked(IGonkGraphicBufferConsumer::BufferItem *item,
+ nsecs_t presentWhen);
+
+ // releaseBufferLocked relinquishes control over a buffer, returning that
+ // control to the GonkBufferQueue.
+ //
+ // Derived classes should override this method to perform any cleanup that
+ // must take place when a buffer is released back to the GonkBufferQueue. If
+ // it is overridden the derived class's implementation must call
+ // GonkConsumerBase::releaseBufferLocked.
+ virtual status_t releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer);
+
+ // returns true iff the slot still has the graphicBuffer in it.
+ bool stillTracking(int slot, const sp<GraphicBuffer> graphicBuffer);
+
+ // addReleaseFence* adds the sync points associated with a fence to the set
+ // of sync points that must be reached before the buffer in the given slot
+ // may be used after the slot has been released. This should be called by
+ // derived classes each time some asynchronous work is kicked off that
+ // references the buffer.
+ status_t addReleaseFence(int slot,
+ const sp<GraphicBuffer> graphicBuffer, const sp<Fence>& fence);
+ status_t addReleaseFenceLocked(int slot,
+ const sp<GraphicBuffer> graphicBuffer, const sp<Fence>& fence);
+
+ // Slot contains the information and object references that
+ // GonkConsumerBase maintains about a GonkBufferQueue buffer slot.
+ struct Slot {
+ // mGraphicBuffer is the Gralloc buffer store in the slot or NULL if
+ // no Gralloc buffer is in the slot.
+ sp<GraphicBuffer> 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<Fence> mFence;
+
+ // the frame number of the last acquired frame for this slot
+ uint64_t mFrameNumber;
+ };
+
+ // mSlots stores the buffers that have been allocated by the GonkBufferQueue
+ // for each buffer slot. It is initialized to null pointers, and gets
+ // filled in with the result of GonkBufferQueue::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[GonkBufferQueue::NUM_BUFFER_SLOTS];
+
+ // mAbandoned indicates that the GonkBufferQueue will no longer be used to
+ // consume images buffers pushed to it using the IGraphicBufferProducer
+ // interface. It is initialized to false, and set to true in the abandon
+ // method. A GonkBufferQueue that has been abandoned will return the NO_INIT
+ // error from all IGonkConsumerBase methods capable of returning an error.
+ bool mAbandoned;
+
+ // mName is a string used to identify the GonkConsumerBase 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.
+ wp<FrameAvailableListener> mFrameAvailableListener;
+
+ // The GonkConsumerBase has-a GonkBufferQueue and is responsible for creating this object
+ // if none is supplied
+ sp<GonkBufferQueue> mConsumer;
+
+ // mMutex is the mutex used to prevent concurrent access to the member
+ // variables of GonkConsumerBase objects. It must be locked whenever the
+ // member variables are accessed or when any of the *Locked methods are
+ // called.
+ //
+ // This mutex is intended to be locked by derived classes.
+ mutable Mutex mMutex;
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // NATIVEWINDOW_GONKCONSUMERBASE_H
diff --git a/widget/gonk/nativewindow/GonkConsumerBaseLL.cpp b/widget/gonk/nativewindow/GonkConsumerBaseLL.cpp
new file mode 100644
index 000000000..5b1166b57
--- /dev/null
+++ b/widget/gonk/nativewindow/GonkConsumerBaseLL.cpp
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2014 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+
+#define LOG_TAG "GonkConsumerBase"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+//#define LOG_NDEBUG 0
+
+#define EGL_EGLEXT_PROTOTYPES
+
+#include <hardware/hardware.h>
+
+#include <gui/IGraphicBufferAlloc.h>
+
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+#include "GonkConsumerBaseLL.h"
+
+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);
+}
+
+GonkConsumerBase::GonkConsumerBase(const sp<IGonkGraphicBufferConsumer>& bufferQueue, bool controlledByApp) :
+ mAbandoned(false),
+ mConsumer(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<ConsumerListener> listener = static_cast<ConsumerListener*>(this);
+ sp<IConsumerListener> proxy = new GonkBufferQueue::ProxyConsumerListener(listener);
+
+ status_t err = mConsumer->consumerConnect(proxy, controlledByApp);
+ if (err != NO_ERROR) {
+ ALOGE("GonkConsumerBase: error connecting to GonkBufferQueue: %s (%d)",
+ strerror(-err), err);
+ } else {
+ mConsumer->setConsumerName(mName);
+ }
+}
+
+GonkConsumerBase::~GonkConsumerBase() {
+ ALOGV("~GonkConsumerBase");
+ Mutex::Autolock lock(mMutex);
+
+ // Verify that abandon() has been called before we get here. This should
+ // be done by GonkConsumerBase::onLastStrongRef(), but it's possible for a
+ // derived class to override that method and not call
+ // GonkConsumerBase::onLastStrongRef().
+ LOG_ALWAYS_FATAL_IF(!mAbandoned, "[%s] ~GonkConsumerBase was called, but the "
+ "consumer is not abandoned!", mName.string());
+}
+
+void GonkConsumerBase::onLastStrongRef(const void* id __attribute__((unused))) {
+ abandon();
+}
+
+void GonkConsumerBase::freeBufferLocked(int slotIndex) {
+ ALOGV("freeBufferLocked: slotIndex=%d", slotIndex);
+ mSlots[slotIndex].mGraphicBuffer = 0;
+ mSlots[slotIndex].mFence = Fence::NO_FENCE;
+ mSlots[slotIndex].mFrameNumber = 0;
+}
+
+#if ANDROID_VERSION == 21
+void GonkConsumerBase::onFrameAvailable() {
+#else
+void GonkConsumerBase::onFrameAvailable(const ::android::BufferItem& item) {
+#endif
+ ALOGV("onFrameAvailable");
+
+ sp<FrameAvailableListener> listener;
+ { // scope for the lock
+ Mutex::Autolock lock(mMutex);
+ listener = mFrameAvailableListener.promote();
+ }
+
+ if (listener != NULL) {
+ ALOGV("actually calling onFrameAvailable");
+ listener->onFrameAvailable();
+ }
+}
+
+void GonkConsumerBase::onBuffersReleased() {
+ Mutex::Autolock lock(mMutex);
+
+ ALOGV("onBuffersReleased");
+
+ if (mAbandoned) {
+ // Nothing to do if we're already abandoned.
+ return;
+ }
+
+ uint64_t mask = 0;
+ mConsumer->getReleasedBuffers(&mask);
+ for (int i = 0; i < GonkBufferQueue::NUM_BUFFER_SLOTS; i++) {
+ if (mask & (1ULL << i)) {
+ freeBufferLocked(i);
+ }
+ }
+}
+
+void GonkConsumerBase::onSidebandStreamChanged() {
+}
+
+void GonkConsumerBase::abandon() {
+ ALOGV("abandon");
+ Mutex::Autolock lock(mMutex);
+
+ if (!mAbandoned) {
+ abandonLocked();
+ mAbandoned = true;
+ }
+}
+
+void GonkConsumerBase::abandonLocked() {
+ ALOGV("abandonLocked");
+ for (int i =0; i < GonkBufferQueue::NUM_BUFFER_SLOTS; i++) {
+ freeBufferLocked(i);
+ }
+ // disconnect from the BufferQueue
+ mConsumer->consumerDisconnect();
+ mConsumer.clear();
+}
+
+void GonkConsumerBase::setFrameAvailableListener(
+ const wp<FrameAvailableListener>& listener) {
+ ALOGV("setFrameAvailableListener");
+ Mutex::Autolock lock(mMutex);
+ mFrameAvailableListener = listener;
+}
+
+void GonkConsumerBase::dump(String8& result) const {
+ dump(result, "");
+}
+
+void GonkConsumerBase::dump(String8& result, const char* prefix) const {
+ Mutex::Autolock _l(mMutex);
+ dumpLocked(result, prefix);
+}
+
+void GonkConsumerBase::dumpLocked(String8& result, const char* prefix) const {
+ result.appendFormat("%smAbandoned=%d\n", prefix, int(mAbandoned));
+
+ if (!mAbandoned) {
+ mConsumer->dumpToString(result, prefix);
+ }
+}
+
+status_t GonkConsumerBase::acquireBufferLocked(GonkBufferQueue::BufferItem *item,
+ nsecs_t presentWhen) {
+ status_t err = mConsumer->acquireBuffer(item, presentWhen);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ if (item->mGraphicBuffer != NULL) {
+ mSlots[item->mBuf].mGraphicBuffer = item->mGraphicBuffer;
+ }
+
+ mSlots[item->mBuf].mFrameNumber = item->mFrameNumber;
+ mSlots[item->mBuf].mFence = item->mFence;
+
+ ALOGV("acquireBufferLocked: -> slot=%d/%" PRIu64,
+ item->mBuf, item->mFrameNumber);
+
+ return OK;
+}
+
+status_t GonkConsumerBase::addReleaseFence(int slot,
+ const sp<GraphicBuffer> graphicBuffer, const sp<Fence>& fence) {
+ Mutex::Autolock lock(mMutex);
+ return addReleaseFenceLocked(slot, graphicBuffer, fence);
+}
+
+status_t GonkConsumerBase::addReleaseFenceLocked(int slot,
+ const sp<GraphicBuffer> graphicBuffer, const sp<Fence>& fence) {
+ ALOGV("addReleaseFenceLocked: slot=%d", slot);
+
+ // If consumer no longer tracks this graphicBuffer, we can safely
+ // drop this fence, as it will never be received by the producer.
+ if (!stillTracking(slot, graphicBuffer)) {
+ return OK;
+ }
+
+ if (!mSlots[slot].mFence.get()) {
+ mSlots[slot].mFence = fence;
+ } else {
+ sp<Fence> mergedFence = Fence::merge(
+ String8::format("%.28s:%d", mName.string(), slot),
+ mSlots[slot].mFence, fence);
+ if (!mergedFence.get()) {
+ ALOGE("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
+ mSlots[slot].mFence = fence;
+ return BAD_VALUE;
+ }
+ mSlots[slot].mFence = mergedFence;
+ }
+
+ return OK;
+}
+
+status_t GonkConsumerBase::releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer) {
+ // If consumer no longer tracks this graphicBuffer (we received a new
+ // buffer on the same slot), the buffer producer is definitely no longer
+ // tracking it.
+ if (!stillTracking(slot, graphicBuffer)) {
+ return OK;
+ }
+
+ ALOGV("releaseBufferLocked: slot=%d/%" PRIu64,
+ slot, mSlots[slot].mFrameNumber);
+ status_t err = mConsumer->releaseBuffer(slot, mSlots[slot].mFrameNumber, mSlots[slot].mFence);
+ if (err == IGonkGraphicBufferConsumer::STALE_BUFFER_SLOT) {
+ freeBufferLocked(slot);
+ }
+
+ mSlots[slot].mFence = Fence::NO_FENCE;
+
+ return err;
+}
+
+bool GonkConsumerBase::stillTracking(int slot,
+ const sp<GraphicBuffer> graphicBuffer) {
+ if (slot < 0 || slot >= GonkBufferQueue::NUM_BUFFER_SLOTS) {
+ return false;
+ }
+ return (mSlots[slot].mGraphicBuffer != NULL &&
+ mSlots[slot].mGraphicBuffer->handle == graphicBuffer->handle);
+}
+
+} // namespace android
diff --git a/widget/gonk/nativewindow/GonkConsumerBaseLL.h b/widget/gonk/nativewindow/GonkConsumerBaseLL.h
new file mode 100644
index 000000000..0b2c2d166
--- /dev/null
+++ b/widget/gonk/nativewindow/GonkConsumerBaseLL.h
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2014 Mozilla Foundation
+ *
+ * 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 NATIVEWINDOW_GONKCONSUMERBASE_LL_H
+#define NATIVEWINDOW_GONKCONSUMERBASE_LL_H
+
+#include <ui/GraphicBuffer.h>
+
+#include <utils/String8.h>
+#include <utils/Vector.h>
+#include <utils/threads.h>
+#include <gui/IConsumerListener.h>
+
+#include "GonkBufferQueueLL.h"
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+class String8;
+
+// GonkConsumerBase is a base class for GonkBufferQueue consumer end-points. It
+// handles common tasks like management of the connection to the GonkBufferQueue
+// and the buffer pool.
+class GonkConsumerBase : public virtual RefBase,
+ protected 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 ~GonkConsumerBase();
+
+ // abandon frees all the buffers and puts the GonkConsumerBase into the
+ // 'abandoned' state. Once put in this state the GonkConsumerBase can never
+ // leave it. When in the 'abandoned' state, all methods of the
+ // IGraphicBufferProducer 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 GonkConsumerBase, 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 GonkConsumerBase that will be used to identify it in
+ // log messages.
+ void setName(const String8& name);
+
+ // dump writes the current state to a string. Child classes should add
+ // their state to the dump by overriding 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) const;
+
+ // setFrameAvailableListener sets the listener object that will be notified
+ // when a new frame becomes available.
+ void setFrameAvailableListener(const wp<FrameAvailableListener>& listener);
+
+private:
+ GonkConsumerBase(const GonkConsumerBase&);
+ void operator=(const GonkConsumerBase&);
+
+protected:
+ // GonkConsumerBase constructs a new GonkConsumerBase object to consume image
+ // buffers from the given IGonkGraphicBufferConsumer.
+ // The controlledByApp flag indicates that this consumer is under the application's
+ // control.
+ GonkConsumerBase(const sp<IGonkGraphicBufferConsumer>& consumer, bool controlledByApp = false);
+
+ // onLastStrongRef gets called by RefBase just before the dtor of the most
+ // derived class. It is used to clean up the buffers so that GonkConsumerBase
+ // can coordinate the clean-up by calling into virtual methods implemented
+ // by the derived classes. This would not be possible from the
+ // ConsuemrBase dtor because by the time that gets called the derived
+ // classes have already been destructed.
+ //
+ // This methods should not need to be overridden by derived classes, but
+ // if they are overridden the GonkConsumerBase implementation must be called
+ // from the derived class.
+ virtual void onLastStrongRef(const void* id);
+
+ // Implementation of the IConsumerListener interface. These
+ // calls are used to notify the GonkConsumerBase of asynchronous events in the
+ // GonkBufferQueue. The onFrameAvailable and onBuffersReleased methods should
+ // not need to be overridden by derived classes, but if they are overridden
+ // the GonkConsumerBase implementation must be called from the derived class.
+ // The GonkConsumerBase version of onSidebandStreamChanged does nothing and can
+ // be overriden by derived classes if they want the notification.
+#if ANDROID_VERSION == 21
+ virtual void onFrameAvailable();
+#else
+ virtual void onFrameAvailable(const ::android::BufferItem& item);
+ virtual void onFrameReplaced(const ::android::BufferItem& item) {};
+#endif
+ virtual void onBuffersReleased();
+ virtual void onSidebandStreamChanged();
+
+ // freeBufferLocked frees up the given buffer slot. If the slot has been
+ // initialized this will release the reference to the GraphicBuffer in that
+ // slot. Otherwise it has no effect.
+ //
+ // Derived classes should override this method to clean up any state they
+ // keep per slot. If it is overridden, the derived class's implementation
+ // must call GonkConsumerBase::freeBufferLocked.
+ //
+ // This method must be called with mMutex locked.
+ virtual void freeBufferLocked(int slotIndex);
+
+ // abandonLocked puts the GonkBufferQueue 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.
+ //
+ // Derived classes should override this method to clean up any object
+ // state they keep (as opposed to per-slot state). If it is overridden,
+ // the derived class's implementation must call GonkConsumerBase::abandonLocked.
+ //
+ // This method must be called with mMutex locked.
+ virtual void abandonLocked();
+
+ // dumpLocked dumps the current state of the GonkConsumerBase object to the
+ // result string. Each line is prefixed with the string pointed to by the
+ // prefix argument. The buffer argument points to a buffer that may be
+ // used for intermediate formatting data, and the size of that buffer is
+ // indicated by the size argument.
+ //
+ // Derived classes should override this method to dump their internal
+ // state. If this method is overridden the derived class's implementation
+ // should call GonkConsumerBase::dumpLocked.
+ //
+ // This method must be called with mMutex locked.
+ virtual void dumpLocked(String8& result, const char* prefix) const;
+
+ // acquireBufferLocked fetches the next buffer from the GonkBufferQueue and
+ // updates the buffer slot for the buffer returned.
+ //
+ // Derived classes should override this method to perform any
+ // initialization that must take place the first time a buffer is assigned
+ // to a slot. If it is overridden the derived class's implementation must
+ // call GonkConsumerBase::acquireBufferLocked.
+ virtual status_t acquireBufferLocked(IGonkGraphicBufferConsumer::BufferItem *item,
+ nsecs_t presentWhen);
+
+ // releaseBufferLocked relinquishes control over a buffer, returning that
+ // control to the GonkBufferQueue.
+ //
+ // Derived classes should override this method to perform any cleanup that
+ // must take place when a buffer is released back to the GonkBufferQueue. If
+ // it is overridden the derived class's implementation must call
+ // GonkConsumerBase::releaseBufferLocked.
+ virtual status_t releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer);
+
+ // returns true iff the slot still has the graphicBuffer in it.
+ bool stillTracking(int slot, const sp<GraphicBuffer> graphicBuffer);
+
+ // addReleaseFence* adds the sync points associated with a fence to the set
+ // of sync points that must be reached before the buffer in the given slot
+ // may be used after the slot has been released. This should be called by
+ // derived classes each time some asynchronous work is kicked off that
+ // references the buffer.
+ status_t addReleaseFence(int slot,
+ const sp<GraphicBuffer> graphicBuffer, const sp<Fence>& fence);
+ status_t addReleaseFenceLocked(int slot,
+ const sp<GraphicBuffer> graphicBuffer, const sp<Fence>& fence);
+
+ // Slot contains the information and object references that
+ // GonkConsumerBase maintains about a GonkBufferQueue buffer slot.
+ struct Slot {
+ // mGraphicBuffer is the Gralloc buffer store in the slot or NULL if
+ // no Gralloc buffer is in the slot.
+ sp<GraphicBuffer> 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<Fence> mFence;
+
+ // the frame number of the last acquired frame for this slot
+ uint64_t mFrameNumber;
+ };
+
+ // mSlots stores the buffers that have been allocated by the GonkBufferQueue
+ // for each buffer slot. It is initialized to null pointers, and gets
+ // filled in with the result of GonkBufferQueue::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[GonkBufferQueue::NUM_BUFFER_SLOTS];
+
+ // mAbandoned indicates that the GonkBufferQueue will no longer be used to
+ // consume images buffers pushed to it using the IGraphicBufferProducer
+ // interface. It is initialized to false, and set to true in the abandon
+ // method. A GonkBufferQueue 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 GonkConsumerBase 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.
+ wp<FrameAvailableListener> mFrameAvailableListener;
+
+ // The GonkConsumerBase has-a GonkBufferQueue and is responsible for creating this object
+ // if none is supplied
+ sp<IGonkGraphicBufferConsumer> mConsumer;
+
+ // mMutex is the mutex used to prevent concurrent access to the member
+ // variables of GonkConsumerBase objects. It must be locked whenever the
+ // member variables are accessed or when any of the *Locked methods are
+ // called.
+ //
+ // This mutex is intended to be locked by derived classes.
+ mutable Mutex mMutex;
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // NATIVEWINDOW_GONKCONSUMERBASE_LL_H
diff --git a/widget/gonk/nativewindow/GonkNativeWindow.h b/widget/gonk/nativewindow/GonkNativeWindow.h
new file mode 100644
index 000000000..61b6780b8
--- /dev/null
+++ b/widget/gonk/nativewindow/GonkNativeWindow.h
@@ -0,0 +1,22 @@
+/* Copyright 2013 Mozilla Foundation and Mozilla contributors
+ *
+ * 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.
+ */
+
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21
+# include "GonkNativeWindowLL.h"
+#elif defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 19
+# include "GonkNativeWindowKK.h"
+#elif defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
+# include "GonkNativeWindowJB.h"
+#endif
diff --git a/widget/gonk/nativewindow/GonkNativeWindowJB.cpp b/widget/gonk/nativewindow/GonkNativeWindowJB.cpp
new file mode 100644
index 000000000..e38642009
--- /dev/null
+++ b/widget/gonk/nativewindow/GonkNativeWindowJB.cpp
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2013 Mozilla Foundation
+ *
+ * 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_NDEBUG 0
+#define LOG_TAG "GonkNativeWindow"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <utils/Log.h>
+
+#include "GonkNativeWindowJB.h"
+#include "GrallocImages.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+#include "mozilla/RefPtr.h"
+
+#define BI_LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
+#define BI_LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
+#define BI_LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
+#define BI_LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
+#define BI_LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
+
+using namespace mozilla;
+using namespace mozilla::layers;
+
+namespace android {
+
+GonkNativeWindow::GonkNativeWindow(int bufferCount) :
+ GonkConsumerBase(new GonkBufferQueue(true) ),
+ mNewFrameCallback(nullptr)
+{
+ mBufferQueue->setMaxAcquiredBufferCount(bufferCount);
+}
+
+GonkNativeWindow::~GonkNativeWindow() {
+}
+
+void GonkNativeWindow::setName(const String8& name) {
+ Mutex::Autolock _l(mMutex);
+ mName = name;
+ mBufferQueue->setConsumerName(name);
+}
+#if ANDROID_VERSION >= 18
+status_t GonkNativeWindow::acquireBuffer(BufferItem *item, bool waitForFence) {
+ status_t err;
+
+ if (!item) return BAD_VALUE;
+
+ Mutex::Autolock _l(mMutex);
+
+ err = acquireBufferLocked(item);
+ if (err != OK) {
+ if (err != NO_BUFFER_AVAILABLE) {
+ BI_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err);
+ }
+ return err;
+ }
+
+ if (waitForFence) {
+ err = item->mFence->waitForever("GonkNativeWindow::acquireBuffer");
+ if (err != OK) {
+ BI_LOGE("Failed to wait for fence of acquired buffer: %s (%d)",
+ strerror(-err), err);
+ return err;
+ }
+ }
+
+ item->mGraphicBuffer = mSlots[item->mBuf].mGraphicBuffer;
+
+ return OK;
+}
+
+status_t GonkNativeWindow::releaseBuffer(const BufferItem &item,
+ const sp<Fence>& releaseFence) {
+ status_t err;
+
+ Mutex::Autolock _l(mMutex);
+
+ err = addReleaseFenceLocked(item.mBuf, releaseFence);
+
+ err = releaseBufferLocked(item.mBuf);
+ if (err != OK) {
+ BI_LOGE("Failed to release buffer: %s (%d)",
+ strerror(-err), err);
+ }
+ return err;
+}
+#endif
+
+status_t GonkNativeWindow::setDefaultBufferSize(uint32_t w, uint32_t h) {
+ Mutex::Autolock _l(mMutex);
+ return mBufferQueue->setDefaultBufferSize(w, h);
+}
+
+status_t GonkNativeWindow::setDefaultBufferFormat(uint32_t defaultFormat) {
+ Mutex::Autolock _l(mMutex);
+ return mBufferQueue->setDefaultBufferFormat(defaultFormat);
+}
+
+already_AddRefed<TextureClient>
+GonkNativeWindow::getCurrentBuffer() {
+ Mutex::Autolock _l(mMutex);
+ GonkBufferQueue::BufferItem item;
+
+ // In asynchronous mode the list is guaranteed to be one buffer
+ // deep, while in synchronous mode we use the oldest buffer.
+ status_t err = acquireBufferLocked(&item);
+ if (err != NO_ERROR) {
+ return NULL;
+ }
+
+ RefPtr<TextureClient> textureClient =
+ mBufferQueue->getTextureClientFromBuffer(item.mGraphicBuffer.get());
+ if (!textureClient) {
+ return NULL;
+ }
+ textureClient->SetRecycleCallback(GonkNativeWindow::RecycleCallback, this);
+ return textureClient.forget();
+}
+
+/* static */ void
+GonkNativeWindow::RecycleCallback(TextureClient* client, void* closure) {
+ GonkNativeWindow* nativeWindow =
+ static_cast<GonkNativeWindow*>(closure);
+
+ MOZ_ASSERT(client && !client->IsDead());
+ client->ClearRecycleCallback();
+ nativeWindow->returnBuffer(client);
+}
+
+void GonkNativeWindow::returnBuffer(TextureClient* client) {
+ BI_LOGD("GonkNativeWindow::returnBuffer");
+ Mutex::Autolock lock(mMutex);
+
+ int index = mBufferQueue->getSlotFromTextureClientLocked(client);
+ if (index < 0) {
+ }
+
+ FenceHandle handle = client->GetAndResetReleaseFenceHandle();
+ RefPtr<FenceHandle::FdObj> fdObj = handle.GetAndResetFdObj();
+ sp<Fence> fence = new Fence(fdObj->GetAndResetFd());
+
+ addReleaseFenceLocked(index, fence);
+
+ releaseBufferLocked(index);
+}
+
+already_AddRefed<TextureClient>
+GonkNativeWindow::getTextureClientFromBuffer(ANativeWindowBuffer* buffer) {
+ Mutex::Autolock lock(mMutex);
+ return mBufferQueue->getTextureClientFromBuffer(buffer);
+}
+
+void GonkNativeWindow::setNewFrameCallback(
+ GonkNativeWindowNewFrameCallback* callback) {
+ BI_LOGD("setNewFrameCallback");
+ Mutex::Autolock lock(mMutex);
+ mNewFrameCallback = callback;
+}
+
+void GonkNativeWindow::onFrameAvailable() {
+ GonkConsumerBase::onFrameAvailable();
+
+ if (mNewFrameCallback) {
+ mNewFrameCallback->OnNewFrame();
+ }
+}
+
+} // namespace android
diff --git a/widget/gonk/nativewindow/GonkNativeWindowJB.h b/widget/gonk/nativewindow/GonkNativeWindowJB.h
new file mode 100644
index 000000000..e63a7527d
--- /dev/null
+++ b/widget/gonk/nativewindow/GonkNativeWindowJB.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2013 Mozilla Foundation
+ *
+ * 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 NATIVEWINDOW_GONKNATIVEWINDOW_JB_H
+#define NATIVEWINDOW_GONKNATIVEWINDOW_JB_H
+
+#include <ui/GraphicBuffer.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+#include <utils/threads.h>
+
+#include "GonkConsumerBaseJB.h"
+#include "GrallocImages.h"
+#include "mozilla/layers/LayersSurfaces.h"
+
+namespace mozilla {
+namespace layers {
+ class PGrallocBufferChild;
+}
+}
+
+namespace android {
+
+// The user of GonkNativeWindow who wants to receive notification of
+// new frames should implement this interface.
+class GonkNativeWindowNewFrameCallback {
+public:
+ virtual void OnNewFrame() = 0;
+};
+
+/**
+ * GonkNativeWindow is a GonkBufferQueue consumer endpoint that allows clients
+ * access to the whole BufferItem entry from GonkBufferQueue. Multiple buffers may
+ * be acquired at once, to be used concurrently by the client. This consumer can
+ * operate either in synchronous or asynchronous mode.
+ */
+class GonkNativeWindow: public GonkConsumerBase
+{
+ typedef mozilla::layers::TextureClient TextureClient;
+ public:
+ typedef GonkConsumerBase::FrameAvailableListener FrameAvailableListener;
+
+ typedef GonkBufferQueue::BufferItem BufferItem;
+
+ enum { INVALID_BUFFER_SLOT = GonkBufferQueue::INVALID_BUFFER_SLOT };
+ enum { NO_BUFFER_AVAILABLE = GonkBufferQueue::NO_BUFFER_AVAILABLE };
+
+ // Create a new buffer item consumer. The consumerUsage parameter determines
+ // the consumer usage flags passed to the graphics allocator. The
+ // bufferCount parameter specifies how many buffers can be locked for user
+ // access at the same time.
+ GonkNativeWindow(int bufferCount = GonkBufferQueue::MIN_UNDEQUEUED_BUFFERS);
+
+ virtual ~GonkNativeWindow();
+
+ // set the name of the GonkNativeWindow that will be used to identify it in
+ // log messages.
+ void setName(const String8& name);
+
+ // Gets the next graphics buffer from the producer, filling out the
+ // passed-in BufferItem structure. Returns NO_BUFFER_AVAILABLE if the queue
+ // of buffers is empty, and INVALID_OPERATION if the maximum number of
+ // buffers is already acquired.
+ //
+ // Only a fixed number of buffers can be acquired at a time, determined by
+ // the construction-time bufferCount parameter. If INVALID_OPERATION is
+ // returned by acquireBuffer, then old buffers must be returned to the
+ // queue by calling releaseBuffer before more buffers can be acquired.
+ //
+ // If waitForFence is true, and the acquired BufferItem has a valid fence object,
+ // acquireBuffer will wait on the fence with no timeout before returning.
+#if ANDROID_VERSION >= 18
+ status_t acquireBuffer(BufferItem *item, bool waitForFence = true);
+#endif
+ // Returns an acquired buffer to the queue, allowing it to be reused. Since
+ // only a fixed number of buffers may be acquired at a time, old buffers
+ // must be released by calling releaseBuffer to ensure new buffers can be
+ // acquired by acquireBuffer. Once a BufferItem is released, the caller must
+ // not access any members of the BufferItem, and should immediately remove
+ // all of its references to the BufferItem itself.
+#if ANDROID_VERSION >= 18
+ status_t releaseBuffer(const BufferItem &item,
+ const sp<Fence>& releaseFence = Fence::NO_FENCE);
+#endif
+
+ sp<IGraphicBufferProducer> getProducerInterface() const { return getBufferQueue(); }
+
+ // setDefaultBufferSize is used to set the size of buffers returned by
+ // requestBuffers when a with and height of zero is requested.
+ status_t setDefaultBufferSize(uint32_t w, uint32_t h);
+
+ // setDefaultBufferFormat allows the BufferQueue to create
+ // GraphicBuffers of a defaultFormat if no format is specified
+ // in dequeueBuffer
+ status_t setDefaultBufferFormat(uint32_t defaultFormat);
+
+ // Get next frame from the queue, caller owns the returned buffer.
+ already_AddRefed<TextureClient> getCurrentBuffer();
+
+ // Return the buffer to the queue and mark it as FREE. After that
+ // the buffer is useable again for the decoder.
+ void returnBuffer(TextureClient* client);
+
+ already_AddRefed<TextureClient> getTextureClientFromBuffer(ANativeWindowBuffer* buffer);
+
+ void setNewFrameCallback(GonkNativeWindowNewFrameCallback* callback);
+
+ static void RecycleCallback(TextureClient* client, void* closure);
+
+protected:
+ virtual void onFrameAvailable();
+
+private:
+ GonkNativeWindowNewFrameCallback* mNewFrameCallback;
+};
+
+} // namespace android
+
+#endif // NATIVEWINDOW_GONKNATIVEWINDOW_JB_H
diff --git a/widget/gonk/nativewindow/GonkNativeWindowKK.cpp b/widget/gonk/nativewindow/GonkNativeWindowKK.cpp
new file mode 100644
index 000000000..cf34d6539
--- /dev/null
+++ b/widget/gonk/nativewindow/GonkNativeWindowKK.cpp
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2013 Mozilla Foundation
+ *
+ * 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_NDEBUG 0
+#define LOG_TAG "GonkNativeWindow"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <utils/Log.h>
+
+#include "GonkNativeWindowKK.h"
+#include "GrallocImages.h"
+
+using namespace mozilla;
+using namespace mozilla::layers;
+
+namespace android {
+
+GonkNativeWindow::GonkNativeWindow(int bufferCount) :
+ GonkConsumerBase(new GonkBufferQueue(true), false),
+ mNewFrameCallback(nullptr)
+{
+ mConsumer->setMaxAcquiredBufferCount(bufferCount);
+}
+
+GonkNativeWindow::GonkNativeWindow(const sp<GonkBufferQueue>& bq,
+ uint32_t consumerUsage, int bufferCount, bool controlledByApp) :
+ GonkConsumerBase(bq, controlledByApp)
+{
+ mConsumer->setConsumerUsageBits(consumerUsage);
+ mConsumer->setMaxAcquiredBufferCount(bufferCount);
+}
+
+GonkNativeWindow::~GonkNativeWindow() {
+}
+
+void GonkNativeWindow::setName(const String8& name) {
+ Mutex::Autolock _l(mMutex);
+ mName = name;
+ mConsumer->setConsumerName(name);
+}
+
+status_t GonkNativeWindow::acquireBuffer(BufferItem *item,
+ nsecs_t presentWhen, bool waitForFence) {
+ status_t err;
+
+ if (!item) return BAD_VALUE;
+
+ Mutex::Autolock _l(mMutex);
+
+ err = acquireBufferLocked(item, presentWhen);
+ if (err != OK) {
+ if (err != NO_BUFFER_AVAILABLE) {
+ ALOGE("Error acquiring buffer: %s (%d)", strerror(err), err);
+ }
+ return err;
+ }
+
+ if (waitForFence) {
+ err = item->mFence->waitForever("GonkNativeWindow::acquireBuffer");
+ if (err != OK) {
+ ALOGE("Failed to wait for fence of acquired buffer: %s (%d)",
+ strerror(-err), err);
+ return err;
+ }
+ }
+
+ item->mGraphicBuffer = mSlots[item->mBuf].mGraphicBuffer;
+
+ return OK;
+}
+
+status_t GonkNativeWindow::releaseBuffer(const BufferItem &item,
+ const sp<Fence>& releaseFence) {
+ status_t err;
+
+ Mutex::Autolock _l(mMutex);
+
+ err = addReleaseFenceLocked(item.mBuf, item.mGraphicBuffer, releaseFence);
+
+ err = releaseBufferLocked(item.mBuf, item.mGraphicBuffer);
+ if (err != OK) {
+ ALOGE("Failed to release buffer: %s (%d)",
+ strerror(-err), err);
+ }
+ return err;
+}
+
+status_t GonkNativeWindow::setDefaultBufferSize(uint32_t w, uint32_t h) {
+ Mutex::Autolock _l(mMutex);
+ return mConsumer->setDefaultBufferSize(w, h);
+}
+
+status_t GonkNativeWindow::setDefaultBufferFormat(uint32_t defaultFormat) {
+ Mutex::Autolock _l(mMutex);
+ return mConsumer->setDefaultBufferFormat(defaultFormat);
+}
+
+already_AddRefed<TextureClient>
+GonkNativeWindow::getCurrentBuffer() {
+ Mutex::Autolock _l(mMutex);
+ BufferItem item;
+
+ // In asynchronous mode the list is guaranteed to be one buffer
+ // deep, while in synchronous mode we use the oldest buffer.
+ status_t err = acquireBufferLocked(&item, 0); //???
+ if (err != NO_ERROR) {
+ return NULL;
+ }
+
+ RefPtr<TextureClient> textureClient =
+ mConsumer->getTextureClientFromBuffer(item.mGraphicBuffer.get());
+ if (!textureClient) {
+ return NULL;
+ }
+ textureClient->SetRecycleCallback(GonkNativeWindow::RecycleCallback, this);
+ return textureClient.forget();
+}
+
+/* static */ void
+GonkNativeWindow::RecycleCallback(TextureClient* client, void* closure) {
+ GonkNativeWindow* nativeWindow =
+ static_cast<GonkNativeWindow*>(closure);
+
+ MOZ_ASSERT(client && !client->IsDead());
+ client->ClearRecycleCallback();
+ nativeWindow->returnBuffer(client);
+}
+
+void GonkNativeWindow::returnBuffer(TextureClient* client) {
+ ALOGD("GonkNativeWindow::returnBuffer");
+ Mutex::Autolock lock(mMutex);
+
+ int index = mConsumer->getSlotFromTextureClientLocked(client);
+ if (index < 0) {
+ }
+
+ FenceHandle handle = client->GetAndResetReleaseFenceHandle();
+ RefPtr<FenceHandle::FdObj> fdObj = handle.GetAndResetFdObj();
+ sp<Fence> fence = new Fence(fdObj->GetAndResetFd());
+
+ addReleaseFenceLocked(index,
+ mSlots[index].mGraphicBuffer,
+ fence);
+
+ releaseBufferLocked(index, mSlots[index].mGraphicBuffer);
+}
+
+already_AddRefed<TextureClient>
+GonkNativeWindow::getTextureClientFromBuffer(ANativeWindowBuffer* buffer) {
+ Mutex::Autolock lock(mMutex);
+ return mConsumer->getTextureClientFromBuffer(buffer);
+}
+
+void GonkNativeWindow::setNewFrameCallback(
+ GonkNativeWindowNewFrameCallback* callback) {
+ ALOGD("setNewFrameCallback");
+ Mutex::Autolock lock(mMutex);
+ mNewFrameCallback = callback;
+}
+
+void GonkNativeWindow::onFrameAvailable() {
+ GonkConsumerBase::onFrameAvailable();
+
+ if (mNewFrameCallback) {
+ mNewFrameCallback->OnNewFrame();
+ }
+}
+
+} // namespace android
diff --git a/widget/gonk/nativewindow/GonkNativeWindowKK.h b/widget/gonk/nativewindow/GonkNativeWindowKK.h
new file mode 100644
index 000000000..e36788b41
--- /dev/null
+++ b/widget/gonk/nativewindow/GonkNativeWindowKK.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2013 Mozilla Foundation
+ *
+ * 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 NATIVEWINDOW_GONKNATIVEWINDOW_KK_H
+#define NATIVEWINDOW_GONKNATIVEWINDOW_KK_H
+
+#include <ui/GraphicBuffer.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+#include <utils/threads.h>
+
+#include "GonkConsumerBaseKK.h"
+#include "GrallocImages.h"
+#include "IGonkGraphicBufferConsumer.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+#include "mozilla/layers/LayersSurfaces.h"
+
+namespace mozilla {
+namespace layers {
+ class PGrallocBufferChild;
+}
+}
+
+namespace android {
+
+// The user of GonkNativeWindow who wants to receive notification of
+// new frames should implement this interface.
+class GonkNativeWindowNewFrameCallback {
+public:
+ virtual void OnNewFrame() = 0;
+};
+
+/**
+ * GonkNativeWindow is a GonkBufferQueue consumer endpoint that allows clients
+ * access to the whole BufferItem entry from GonkBufferQueue. Multiple buffers may
+ * be acquired at once, to be used concurrently by the client. This consumer can
+ * operate either in synchronous or asynchronous mode.
+ */
+class GonkNativeWindow: public GonkConsumerBase
+{
+ typedef mozilla::layers::TextureClient TextureClient;
+ public:
+ typedef GonkConsumerBase::FrameAvailableListener FrameAvailableListener;
+ typedef IGonkGraphicBufferConsumer::BufferItem BufferItem;
+
+ enum { INVALID_BUFFER_SLOT = GonkBufferQueue::INVALID_BUFFER_SLOT };
+ enum { NO_BUFFER_AVAILABLE = GonkBufferQueue::NO_BUFFER_AVAILABLE };
+
+ // Create a new buffer item consumer. The consumerUsage parameter determines
+ // the consumer usage flags passed to the graphics allocator. The
+ // bufferCount parameter specifies how many buffers can be locked for user
+ // access at the same time.
+ // controlledByApp tells whether this consumer is controlled by the
+ // application.
+ GonkNativeWindow(int bufferCount = GonkBufferQueue::MIN_UNDEQUEUED_BUFFERS);
+ GonkNativeWindow(const sp<GonkBufferQueue>& bq, uint32_t consumerUsage,
+ int bufferCount = GonkBufferQueue::MIN_UNDEQUEUED_BUFFERS,
+ bool controlledByApp = false);
+
+ virtual ~GonkNativeWindow();
+
+ // set the name of the GonkNativeWindow that will be used to identify it in
+ // log messages.
+ void setName(const String8& name);
+
+ // Gets the next graphics buffer from the producer, filling out the
+ // passed-in BufferItem structure. Returns NO_BUFFER_AVAILABLE if the queue
+ // of buffers is empty, and INVALID_OPERATION if the maximum number of
+ // buffers is already acquired.
+ //
+ // Only a fixed number of buffers can be acquired at a time, determined by
+ // the construction-time bufferCount parameter. If INVALID_OPERATION is
+ // returned by acquireBuffer, then old buffers must be returned to the
+ // queue by calling releaseBuffer before more buffers can be acquired.
+ //
+ // If waitForFence is true, and the acquired BufferItem has a valid fence object,
+ // acquireBuffer will wait on the fence with no timeout before returning.
+ status_t acquireBuffer(BufferItem *item, nsecs_t presentWhen,
+ bool waitForFence = true);
+
+ // Returns an acquired buffer to the queue, allowing it to be reused. Since
+ // only a fixed number of buffers may be acquired at a time, old buffers
+ // must be released by calling releaseBuffer to ensure new buffers can be
+ // acquired by acquireBuffer. Once a BufferItem is released, the caller must
+ // not access any members of the BufferItem, and should immediately remove
+ // all of its references to the BufferItem itself.
+ status_t releaseBuffer(const BufferItem &item,
+ const sp<Fence>& releaseFence = Fence::NO_FENCE);
+
+ // setDefaultBufferSize is used to set the size of buffers returned by
+ // requestBuffers when a with and height of zero is requested.
+ status_t setDefaultBufferSize(uint32_t w, uint32_t h);
+
+ // setDefaultBufferFormat allows the BufferQueue to create
+ // GraphicBuffers of a defaultFormat if no format is specified
+ // in dequeueBuffer
+ status_t setDefaultBufferFormat(uint32_t defaultFormat);
+
+ // Get next frame from the queue, caller owns the returned buffer.
+ already_AddRefed<TextureClient> getCurrentBuffer();
+
+ // Return the buffer to the queue and mark it as FREE. After that
+ // the buffer is useable again for the decoder.
+ void returnBuffer(TextureClient* client);
+
+ already_AddRefed<TextureClient> getTextureClientFromBuffer(ANativeWindowBuffer* buffer);
+
+ void setNewFrameCallback(GonkNativeWindowNewFrameCallback* callback);
+
+ static void RecycleCallback(TextureClient* client, void* closure);
+
+protected:
+ virtual void onFrameAvailable();
+
+private:
+ GonkNativeWindowNewFrameCallback* mNewFrameCallback;
+};
+
+} // namespace android
+
+#endif // NATIVEWINDOW_GONKNATIVEWINDOW_JB_H
diff --git a/widget/gonk/nativewindow/GonkNativeWindowLL.cpp b/widget/gonk/nativewindow/GonkNativeWindowLL.cpp
new file mode 100644
index 000000000..48644a22f
--- /dev/null
+++ b/widget/gonk/nativewindow/GonkNativeWindowLL.cpp
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2014 Mozilla Foundation
+ *
+ * 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_NDEBUG 0
+#define LOG_TAG "GonkNativeWindow"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <utils/Log.h>
+
+#include "GonkNativeWindowLL.h"
+
+using namespace mozilla;
+using namespace mozilla::layers;
+
+namespace android {
+
+GonkNativeWindow::GonkNativeWindow(
+ const sp<IGonkGraphicBufferConsumer>& consumer, int bufferCount) :
+ GonkConsumerBase(consumer, false),
+ mNewFrameCallback(nullptr)
+{
+ if (bufferCount != DEFAULT_MAX_BUFFERS) {
+ status_t err = mConsumer->setMaxAcquiredBufferCount(bufferCount);
+ LOG_ALWAYS_FATAL_IF(err != OK,
+ "Failed to set max acquired buffer count to %d", bufferCount);
+ }
+}
+
+GonkNativeWindow::GonkNativeWindow(
+ const sp<IGonkGraphicBufferConsumer>& consumer, uint32_t consumerUsage,
+ int bufferCount, bool controlledByApp) :
+ GonkConsumerBase(consumer, controlledByApp)
+{
+ status_t err = mConsumer->setConsumerUsageBits(consumerUsage);
+ LOG_ALWAYS_FATAL_IF(err != OK,
+ "Failed to set consumer usage bits to %#x", consumerUsage);
+ if (bufferCount != DEFAULT_MAX_BUFFERS) {
+ err = mConsumer->setMaxAcquiredBufferCount(bufferCount);
+ LOG_ALWAYS_FATAL_IF(err != OK,
+ "Failed to set max acquired buffer count to %d", bufferCount);
+ }
+}
+
+GonkNativeWindow::~GonkNativeWindow() {
+}
+
+void GonkNativeWindow::setName(const String8& name) {
+ Mutex::Autolock _l(mMutex);
+ mName = name;
+ mConsumer->setConsumerName(name);
+}
+
+status_t GonkNativeWindow::acquireBuffer(BufferItem *item,
+ nsecs_t presentWhen, bool waitForFence) {
+ status_t err;
+
+ if (!item) return BAD_VALUE;
+
+ Mutex::Autolock _l(mMutex);
+
+ err = acquireBufferLocked(item, presentWhen);
+ if (err != OK) {
+ if (err != NO_BUFFER_AVAILABLE) {
+ ALOGE("Error acquiring buffer: %s (%d)", strerror(err), err);
+ }
+ return err;
+ }
+
+ if (waitForFence) {
+ err = item->mFence->waitForever("GonkNativeWindow::acquireBuffer");
+ if (err != OK) {
+ ALOGE("Failed to wait for fence of acquired buffer: %s (%d)",
+ strerror(-err), err);
+ return err;
+ }
+ }
+
+ item->mGraphicBuffer = mSlots[item->mBuf].mGraphicBuffer;
+
+ return OK;
+}
+
+status_t GonkNativeWindow::releaseBuffer(const BufferItem &item,
+ const sp<Fence>& releaseFence) {
+ status_t err;
+
+ Mutex::Autolock _l(mMutex);
+
+ err = addReleaseFenceLocked(item.mBuf, item.mGraphicBuffer, releaseFence);
+
+ err = releaseBufferLocked(item.mBuf, item.mGraphicBuffer);
+ if (err != OK) {
+ ALOGE("Failed to release buffer: %s (%d)",
+ strerror(-err), err);
+ }
+ return err;
+}
+
+status_t GonkNativeWindow::setDefaultBufferSize(uint32_t w, uint32_t h) {
+ Mutex::Autolock _l(mMutex);
+ return mConsumer->setDefaultBufferSize(w, h);
+}
+
+status_t GonkNativeWindow::setDefaultBufferFormat(uint32_t defaultFormat) {
+ Mutex::Autolock _l(mMutex);
+ return mConsumer->setDefaultBufferFormat(defaultFormat);
+}
+
+already_AddRefed<TextureClient>
+GonkNativeWindow::getCurrentBuffer() {
+ Mutex::Autolock _l(mMutex);
+ BufferItem item;
+
+ // In asynchronous mode the list is guaranteed to be one buffer
+ // deep, while in synchronous mode we use the oldest buffer.
+ status_t err = acquireBufferLocked(&item, 0); //???
+ if (err != NO_ERROR) {
+ return NULL;
+ }
+
+ RefPtr<TextureClient> textureClient =
+ mConsumer->getTextureClientFromBuffer(item.mGraphicBuffer.get());
+ if (!textureClient) {
+ return NULL;
+ }
+ textureClient->SetRecycleCallback(GonkNativeWindow::RecycleCallback, this);
+ return textureClient.forget();
+}
+
+/* static */ void
+GonkNativeWindow::RecycleCallback(TextureClient* client, void* closure) {
+ GonkNativeWindow* nativeWindow =
+ static_cast<GonkNativeWindow*>(closure);
+
+ MOZ_ASSERT(client && !client->IsDead());
+ client->ClearRecycleCallback();
+ nativeWindow->returnBuffer(client);
+}
+
+void GonkNativeWindow::returnBuffer(TextureClient* client) {
+ ALOGD("GonkNativeWindow::returnBuffer");
+ Mutex::Autolock lock(mMutex);
+
+ int index = mConsumer->getSlotFromTextureClientLocked(client);
+ if (index < 0) {
+ return;
+ }
+
+ FenceHandle handle = client->GetAndResetReleaseFenceHandle();
+ RefPtr<FenceHandle::FdObj> fdObj = handle.GetAndResetFdObj();
+ sp<Fence> fence = new Fence(fdObj->GetAndResetFd());
+
+ status_t err;
+ err = addReleaseFenceLocked(index,
+ mSlots[index].mGraphicBuffer,
+ fence);
+
+ err = releaseBufferLocked(index, mSlots[index].mGraphicBuffer);
+
+ if (err != OK) {
+ ALOGE("Failed to return buffer: %s (%d)", strerror(-err), err);
+ }
+}
+
+already_AddRefed<TextureClient>
+GonkNativeWindow::getTextureClientFromBuffer(ANativeWindowBuffer* buffer) {
+ Mutex::Autolock lock(mMutex);
+ return mConsumer->getTextureClientFromBuffer(buffer);
+}
+
+void GonkNativeWindow::setNewFrameCallback(
+ GonkNativeWindowNewFrameCallback* callback) {
+ ALOGD("setNewFrameCallback");
+ Mutex::Autolock lock(mMutex);
+ mNewFrameCallback = callback;
+}
+
+#if ANDROID_VERSION == 21
+void GonkNativeWindow::onFrameAvailable() {
+ GonkConsumerBase::onFrameAvailable();
+#else
+void GonkNativeWindow::onFrameAvailable(const ::android::BufferItem &item) {
+ GonkConsumerBase::onFrameAvailable(item);
+#endif
+
+ if (mNewFrameCallback) {
+ mNewFrameCallback->OnNewFrame();
+ }
+}
+
+} // namespace android
diff --git a/widget/gonk/nativewindow/GonkNativeWindowLL.h b/widget/gonk/nativewindow/GonkNativeWindowLL.h
new file mode 100644
index 000000000..64cd6482d
--- /dev/null
+++ b/widget/gonk/nativewindow/GonkNativeWindowLL.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2014 Mozilla Foundation
+ *
+ * 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 NATIVEWINDOW_GONKNATIVEWINDOW_LL_H
+#define NATIVEWINDOW_GONKNATIVEWINDOW_LL_H
+
+#include <ui/GraphicBuffer.h>
+
+#include <utils/String8.h>
+#include <utils/Vector.h>
+#include <utils/threads.h>
+
+#include "GonkConsumerBaseLL.h"
+#include "IGonkGraphicBufferConsumerLL.h"
+
+namespace android {
+
+// The user of GonkNativeWindow who wants to receive notification of
+// new frames should implement this interface.
+class GonkNativeWindowNewFrameCallback {
+public:
+ virtual void OnNewFrame() = 0;
+};
+
+/**
+ * GonkNativeWindow is a GonkBufferQueue consumer endpoint that allows clients
+ * access to the whole BufferItem entry from GonkBufferQueue. Multiple buffers may
+ * be acquired at once, to be used concurrently by the client. This consumer can
+ * operate either in synchronous or asynchronous mode.
+ */
+class GonkNativeWindow: public GonkConsumerBase
+{
+ typedef mozilla::layers::TextureClient TextureClient;
+ public:
+ typedef GonkConsumerBase::FrameAvailableListener FrameAvailableListener;
+ typedef GonkBufferQueue::BufferItem BufferItem;
+
+ enum { DEFAULT_MAX_BUFFERS = -1 };
+ enum { INVALID_BUFFER_SLOT = GonkBufferQueue::INVALID_BUFFER_SLOT };
+ enum { NO_BUFFER_AVAILABLE = GonkBufferQueue::NO_BUFFER_AVAILABLE };
+
+ // Create a new buffer item consumer. The consumerUsage parameter determines
+ // the consumer usage flags passed to the graphics allocator. The
+ // bufferCount parameter specifies how many buffers can be locked for user
+ // access at the same time.
+ // controlledByApp tells whether this consumer is controlled by the
+ // application.
+ GonkNativeWindow(const sp<IGonkGraphicBufferConsumer>& consumer,
+ int bufferCount = DEFAULT_MAX_BUFFERS);
+ GonkNativeWindow(const sp<IGonkGraphicBufferConsumer>& consumer,
+ uint32_t consumerUsage, int bufferCount = DEFAULT_MAX_BUFFERS,
+ bool controlledByApp = false);
+
+ virtual ~GonkNativeWindow();
+
+ // set the name of the GonkNativeWindow that will be used to identify it in
+ // log messages.
+ void setName(const String8& name);
+
+ // Gets the next graphics buffer from the producer, filling out the
+ // passed-in BufferItem structure. Returns NO_BUFFER_AVAILABLE if the queue
+ // of buffers is empty, and INVALID_OPERATION if the maximum number of
+ // buffers is already acquired.
+ //
+ // Only a fixed number of buffers can be acquired at a time, determined by
+ // the construction-time bufferCount parameter. If INVALID_OPERATION is
+ // returned by acquireBuffer, then old buffers must be returned to the
+ // queue by calling releaseBuffer before more buffers can be acquired.
+ //
+ // If waitForFence is true, and the acquired BufferItem has a valid fence object,
+ // acquireBuffer will wait on the fence with no timeout before returning.
+ status_t acquireBuffer(BufferItem *item, nsecs_t presentWhen,
+ bool waitForFence = true);
+
+ // Returns an acquired buffer to the queue, allowing it to be reused. Since
+ // only a fixed number of buffers may be acquired at a time, old buffers
+ // must be released by calling releaseBuffer to ensure new buffers can be
+ // acquired by acquireBuffer. Once a BufferItem is released, the caller must
+ // not access any members of the BufferItem, and should immediately remove
+ // all of its references to the BufferItem itself.
+ status_t releaseBuffer(const BufferItem &item,
+ const sp<Fence>& releaseFence = Fence::NO_FENCE);
+
+ // setDefaultBufferSize is used to set the size of buffers returned by
+ // requestBuffers when a with and height of zero is requested.
+ status_t setDefaultBufferSize(uint32_t w, uint32_t h);
+
+ // setDefaultBufferFormat allows the GonkBufferQueue to create
+ // GraphicBuffers of a defaultFormat if no format is specified
+ // in dequeueBuffer
+ status_t setDefaultBufferFormat(uint32_t defaultFormat);
+
+ // Get next frame from the queue, caller owns the returned buffer.
+ already_AddRefed<TextureClient> getCurrentBuffer();
+
+ // Return the buffer to the queue and mark it as FREE. After that
+ // the buffer is useable again for the decoder.
+ void returnBuffer(TextureClient* client);
+
+ already_AddRefed<TextureClient> getTextureClientFromBuffer(ANativeWindowBuffer* buffer);
+
+ void setNewFrameCallback(GonkNativeWindowNewFrameCallback* callback);
+
+ static void RecycleCallback(TextureClient* client, void* closure);
+
+protected:
+#if ANDROID_VERSION == 21
+ virtual void onFrameAvailable();
+#else
+ virtual void onFrameAvailable(const ::android::BufferItem &item);
+#endif
+
+private:
+ GonkNativeWindowNewFrameCallback* mNewFrameCallback;
+};
+
+} // namespace android
+
+#endif // NATIVEWINDOW_GONKNATIVEWINDOW_LL_H
diff --git a/widget/gonk/nativewindow/IGonkGraphicBufferConsumer.h b/widget/gonk/nativewindow/IGonkGraphicBufferConsumer.h
new file mode 100644
index 000000000..14541c9b4
--- /dev/null
+++ b/widget/gonk/nativewindow/IGonkGraphicBufferConsumer.h
@@ -0,0 +1,20 @@
+/* Copyright 2013 Mozilla Foundation and Mozilla contributors
+ *
+ * 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.
+ */
+
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21
+# include "IGonkGraphicBufferConsumerLL.h"
+#elif defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 19
+# include "IGonkGraphicBufferConsumerKK.h"
+#endif
diff --git a/widget/gonk/nativewindow/IGonkGraphicBufferConsumerKK.cpp b/widget/gonk/nativewindow/IGonkGraphicBufferConsumerKK.cpp
new file mode 100644
index 000000000..c4c9f6578
--- /dev/null
+++ b/widget/gonk/nativewindow/IGonkGraphicBufferConsumerKK.cpp
@@ -0,0 +1,480 @@
+/*
+ * Copyright (C) 2013 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 EGL_EGLEXT_PROTOTYPES
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+
+#include <binder/Parcel.h>
+#include <binder/IInterface.h>
+
+#include <gui/IConsumerListener.h>
+#include "IGonkGraphicBufferConsumerKK.h"
+
+#include <ui/GraphicBuffer.h>
+#include <ui/Fence.h>
+
+#include <system/window.h>
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+IGonkGraphicBufferConsumer::BufferItem::BufferItem() :
+ mTransform(0),
+ mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+ mTimestamp(0),
+ mIsAutoTimestamp(false),
+ mFrameNumber(0),
+ mBuf(INVALID_BUFFER_SLOT),
+ mIsDroppable(false),
+ mAcquireCalled(false),
+ mTransformToDisplayInverse(false) {
+ mCrop.makeInvalid();
+}
+
+size_t IGonkGraphicBufferConsumer::BufferItem::getPodSize() const {
+ size_t c = sizeof(mCrop) +
+ sizeof(mTransform) +
+ sizeof(mScalingMode) +
+ sizeof(mTimestamp) +
+ sizeof(mIsAutoTimestamp) +
+ sizeof(mFrameNumber) +
+ sizeof(mBuf) +
+ sizeof(mIsDroppable) +
+ sizeof(mAcquireCalled) +
+ sizeof(mTransformToDisplayInverse);
+ return c;
+}
+
+size_t IGonkGraphicBufferConsumer::BufferItem::getFlattenedSize() const {
+ size_t c = 0;
+ if (mGraphicBuffer != 0) {
+ c += mGraphicBuffer->getFlattenedSize();
+ FlattenableUtils::align<4>(c);
+ }
+ if (mFence != 0) {
+ c += mFence->getFlattenedSize();
+ FlattenableUtils::align<4>(c);
+ }
+ return sizeof(int32_t) + c + getPodSize();
+}
+
+size_t IGonkGraphicBufferConsumer::BufferItem::getFdCount() const {
+ size_t c = 0;
+ if (mGraphicBuffer != 0) {
+ c += mGraphicBuffer->getFdCount();
+ }
+ if (mFence != 0) {
+ c += mFence->getFdCount();
+ }
+ return c;
+}
+
+status_t IGonkGraphicBufferConsumer::BufferItem::flatten(
+ void*& buffer, size_t& size, int*& fds, size_t& count) const {
+
+ // make sure we have enough space
+ if (count < BufferItem::getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ // content flags are stored first
+ uint32_t& flags = *static_cast<uint32_t*>(buffer);
+
+ // advance the pointer
+ FlattenableUtils::advance(buffer, size, sizeof(uint32_t));
+
+ flags = 0;
+ if (mGraphicBuffer != 0) {
+ status_t err = mGraphicBuffer->flatten(buffer, size, fds, count);
+ if (err) return err;
+ size -= FlattenableUtils::align<4>(buffer);
+ flags |= 1;
+ }
+ if (mFence != 0) {
+ status_t err = mFence->flatten(buffer, size, fds, count);
+ if (err) return err;
+ size -= FlattenableUtils::align<4>(buffer);
+ flags |= 2;
+ }
+
+ // check we have enough space (in case flattening the fence/graphicbuffer lied to us)
+ if (size < getPodSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(buffer, size, mCrop);
+ FlattenableUtils::write(buffer, size, mTransform);
+ FlattenableUtils::write(buffer, size, mScalingMode);
+ FlattenableUtils::write(buffer, size, mTimestamp);
+ FlattenableUtils::write(buffer, size, mIsAutoTimestamp);
+ FlattenableUtils::write(buffer, size, mFrameNumber);
+ FlattenableUtils::write(buffer, size, mBuf);
+ FlattenableUtils::write(buffer, size, mIsDroppable);
+ FlattenableUtils::write(buffer, size, mAcquireCalled);
+ FlattenableUtils::write(buffer, size, mTransformToDisplayInverse);
+
+ return NO_ERROR;
+}
+
+status_t IGonkGraphicBufferConsumer::BufferItem::unflatten(
+ void const*& buffer, size_t& size, int const*& fds, size_t& count) {
+
+ if (size < sizeof(uint32_t))
+ return NO_MEMORY;
+
+ uint32_t flags = 0;
+ FlattenableUtils::read(buffer, size, flags);
+
+ if (flags & 1) {
+ mGraphicBuffer = new GraphicBuffer();
+ status_t err = mGraphicBuffer->unflatten(buffer, size, fds, count);
+ if (err) return err;
+ size -= FlattenableUtils::align<4>(buffer);
+ }
+
+ if (flags & 2) {
+ mFence = new Fence();
+ status_t err = mFence->unflatten(buffer, size, fds, count);
+ if (err) return err;
+ size -= FlattenableUtils::align<4>(buffer);
+ }
+
+ // check we have enough space
+ if (size < getPodSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, mCrop);
+ FlattenableUtils::read(buffer, size, mTransform);
+ FlattenableUtils::read(buffer, size, mScalingMode);
+ FlattenableUtils::read(buffer, size, mTimestamp);
+ FlattenableUtils::read(buffer, size, mIsAutoTimestamp);
+ FlattenableUtils::read(buffer, size, mFrameNumber);
+ FlattenableUtils::read(buffer, size, mBuf);
+ FlattenableUtils::read(buffer, size, mIsDroppable);
+ FlattenableUtils::read(buffer, size, mAcquireCalled);
+ FlattenableUtils::read(buffer, size, mTransformToDisplayInverse);
+
+ return NO_ERROR;
+}
+
+// ---------------------------------------------------------------------------
+
+enum {
+ ACQUIRE_BUFFER = IBinder::FIRST_CALL_TRANSACTION,
+ RELEASE_BUFFER,
+ CONSUMER_CONNECT,
+ CONSUMER_DISCONNECT,
+ GET_RELEASED_BUFFERS,
+ SET_DEFAULT_BUFFER_SIZE,
+ SET_DEFAULT_MAX_BUFFER_COUNT,
+ DISABLE_ASYNC_BUFFER,
+ SET_MAX_ACQUIRED_BUFFER_COUNT,
+ SET_CONSUMER_NAME,
+ SET_DEFAULT_BUFFER_FORMAT,
+ SET_CONSUMER_USAGE_BITS,
+ SET_TRANSFORM_HINT,
+ DUMP,
+};
+
+class BpGonkGraphicBufferConsumer : public BpInterface<IGonkGraphicBufferConsumer>
+{
+public:
+ BpGonkGraphicBufferConsumer(const sp<IBinder>& impl)
+ : BpInterface<IGonkGraphicBufferConsumer>(impl)
+ {
+ }
+
+ virtual status_t acquireBuffer(BufferItem *buffer, nsecs_t presentWhen) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor());
+ data.writeInt64(presentWhen);
+ status_t result = remote()->transact(ACQUIRE_BUFFER, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.read(*buffer);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ return reply.readInt32();
+ }
+
+ virtual status_t releaseBuffer(int buf, uint64_t frameNumber,
+ const sp<Fence>& releaseFence) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor());
+ data.writeInt32(buf);
+ data.writeInt64(frameNumber);
+ data.write(*releaseFence);
+ status_t result = remote()->transact(RELEASE_BUFFER, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ return reply.readInt32();
+ }
+
+ virtual status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor());
+ data.writeStrongBinder(consumer->asBinder());
+ data.writeInt32(controlledByApp);
+ status_t result = remote()->transact(CONSUMER_CONNECT, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ return reply.readInt32();
+ }
+
+ virtual status_t consumerDisconnect() {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor());
+ status_t result = remote()->transact(CONSUMER_DISCONNECT, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ return reply.readInt32();
+ }
+
+ virtual status_t getReleasedBuffers(uint32_t* slotMask) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor());
+ status_t result = remote()->transact(GET_RELEASED_BUFFERS, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ *slotMask = reply.readInt32();
+ return reply.readInt32();
+ }
+
+ virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor());
+ data.writeInt32(w);
+ data.writeInt32(h);
+ status_t result = remote()->transact(SET_DEFAULT_BUFFER_SIZE, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ return reply.readInt32();
+ }
+
+ virtual status_t setDefaultMaxBufferCount(int bufferCount) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor());
+ data.writeInt32(bufferCount);
+ status_t result = remote()->transact(SET_DEFAULT_MAX_BUFFER_COUNT, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ return reply.readInt32();
+ }
+
+ virtual status_t disableAsyncBuffer() {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor());
+ status_t result = remote()->transact(DISABLE_ASYNC_BUFFER, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ return reply.readInt32();
+ }
+
+ virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor());
+ data.writeInt32(maxAcquiredBuffers);
+ status_t result = remote()->transact(SET_MAX_ACQUIRED_BUFFER_COUNT, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ return reply.readInt32();
+ }
+
+ virtual void setConsumerName(const String8& name) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor());
+ data.writeString8(name);
+ remote()->transact(SET_CONSUMER_NAME, data, &reply);
+ }
+
+ virtual status_t setDefaultBufferFormat(uint32_t defaultFormat) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor());
+ data.writeInt32(defaultFormat);
+ status_t result = remote()->transact(SET_DEFAULT_BUFFER_FORMAT, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ return reply.readInt32();
+ }
+
+ virtual status_t setConsumerUsageBits(uint32_t usage) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor());
+ data.writeInt32(usage);
+ status_t result = remote()->transact(SET_CONSUMER_USAGE_BITS, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ return reply.readInt32();
+ }
+
+ virtual status_t setTransformHint(uint32_t hint) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor());
+ data.writeInt32(hint);
+ status_t result = remote()->transact(SET_TRANSFORM_HINT, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ return reply.readInt32();
+ }
+
+ virtual void dumpToString(String8& result, const char* prefix) const {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor());
+ data.writeString8(result);
+ data.writeString8(String8(prefix ? prefix : ""));
+ remote()->transact(DUMP, data, &reply);
+ reply.readString8();
+ }
+};
+
+IMPLEMENT_META_INTERFACE(GonkGraphicBufferConsumer, "android.gui.IGonkGraphicBufferConsumer");
+// ----------------------------------------------------------------------
+
+status_t BnGonkGraphicBufferConsumer::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch(code) {
+ case ACQUIRE_BUFFER: {
+ CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply);
+ BufferItem item;
+ int64_t presentWhen = data.readInt64();
+ status_t result = acquireBuffer(&item, presentWhen);
+ status_t err = reply->write(item);
+ if (err) return err;
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
+ case RELEASE_BUFFER: {
+ CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply);
+ int buf = data.readInt32();
+ uint64_t frameNumber = data.readInt64();
+ sp<Fence> releaseFence = new Fence();
+ status_t err = data.read(*releaseFence);
+ if (err) return err;
+ status_t result = releaseBuffer(buf, frameNumber, releaseFence);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
+
+ case CONSUMER_CONNECT: {
+ CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply);
+ sp<IConsumerListener> consumer = IConsumerListener::asInterface( data.readStrongBinder() );
+ bool controlledByApp = data.readInt32();
+ status_t result = consumerConnect(consumer, controlledByApp);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
+
+ case CONSUMER_DISCONNECT: {
+ CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply);
+ status_t result = consumerDisconnect();
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
+ case GET_RELEASED_BUFFERS: {
+ CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply);
+ uint32_t slotMask;
+ status_t result = getReleasedBuffers(&slotMask);
+ reply->writeInt32(slotMask);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
+ case SET_DEFAULT_BUFFER_SIZE: {
+ CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply);
+ uint32_t w = data.readInt32();
+ uint32_t h = data.readInt32();
+ status_t result = setDefaultBufferSize(w, h);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
+ case SET_DEFAULT_MAX_BUFFER_COUNT: {
+ CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply);
+ uint32_t bufferCount = data.readInt32();
+ status_t result = setDefaultMaxBufferCount(bufferCount);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
+ case DISABLE_ASYNC_BUFFER: {
+ CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply);
+ status_t result = disableAsyncBuffer();
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
+ case SET_MAX_ACQUIRED_BUFFER_COUNT: {
+ CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply);
+ uint32_t maxAcquiredBuffers = data.readInt32();
+ status_t result = setMaxAcquiredBufferCount(maxAcquiredBuffers);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
+ case SET_CONSUMER_NAME: {
+ CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply);
+ setConsumerName( data.readString8() );
+ return NO_ERROR;
+ } break;
+ case SET_DEFAULT_BUFFER_FORMAT: {
+ CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply);
+ uint32_t defaultFormat = data.readInt32();
+ status_t result = setDefaultBufferFormat(defaultFormat);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
+ case SET_CONSUMER_USAGE_BITS: {
+ CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply);
+ uint32_t usage = data.readInt32();
+ status_t result = setConsumerUsageBits(usage);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
+ case SET_TRANSFORM_HINT: {
+ CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply);
+ uint32_t hint = data.readInt32();
+ status_t result = setTransformHint(hint);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
+
+ case DUMP: {
+ CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply);
+ String8 result = data.readString8();
+ String8 prefix = data.readString8();
+ static_cast<IGonkGraphicBufferConsumer*>(this)->dumpToString(result, prefix);
+ reply->writeString8(result);
+ return NO_ERROR;
+ }
+ }
+ return BBinder::onTransact(code, data, reply, flags);
+}
+
+}; // namespace android
diff --git a/widget/gonk/nativewindow/IGonkGraphicBufferConsumerKK.h b/widget/gonk/nativewindow/IGonkGraphicBufferConsumerKK.h
new file mode 100644
index 000000000..ce51e1ef2
--- /dev/null
+++ b/widget/gonk/nativewindow/IGonkGraphicBufferConsumerKK.h
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2013 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 NATIVEWINDOW_IGONKGRAPHICBUFFERCONSUMER_KK_H
+#define NATIVEWINDOW_IGONKGRAPHICBUFFERCONSUMER_KK_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/Timers.h>
+
+#include <binder/IInterface.h>
+#include <ui/Rect.h>
+
+#include "mozilla/Types.h"
+#include "mozilla/layers/LayersSurfaces.h"
+
+namespace mozilla {
+
+namespace layers {
+class TextureClient;
+}
+}
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+class MOZ_EXPORT IConsumerListener;
+class MOZ_EXPORT GraphicBuffer;
+class MOZ_EXPORT Fence;
+
+class IGonkGraphicBufferConsumer : public IInterface {
+ typedef mozilla::layers::TextureClient TextureClient;
+public:
+
+ // public facing structure for BufferSlot
+ class BufferItem : public Flattenable<BufferItem> {
+ friend class Flattenable<BufferItem>;
+ size_t getPodSize() const;
+ size_t getFlattenedSize() const;
+ size_t getFdCount() const;
+ status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
+ status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
+
+ public:
+ enum { INVALID_BUFFER_SLOT = -1 };
+ BufferItem();
+
+ // mGraphicBuffer points to the buffer allocated for this slot, or is NULL
+ // if the buffer in this slot has been acquired in the past (see
+ // BufferSlot.mAcquireCalled).
+ sp<GraphicBuffer> mGraphicBuffer;
+
+ // mFence is a fence that will signal when the buffer is idle.
+ sp<Fence> mFence;
+
+ // mCrop is the current crop rectangle for this buffer slot.
+ Rect mCrop;
+
+ // mTransform is the current transform flags for this buffer slot.
+ uint32_t mTransform;
+
+ // mScalingMode is the current scaling mode for this buffer 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;
+
+ // mIsAutoTimestamp indicates whether mTimestamp was generated
+ // automatically when the buffer was queued.
+ bool mIsAutoTimestamp;
+
+ // mFrameNumber is the number of the queued frame for this slot.
+ uint64_t mFrameNumber;
+
+ // mBuf is the slot index of this buffer
+ int mBuf;
+
+ // mIsDroppable whether this buffer was queued with the
+ // property that it can be replaced by a new buffer for the purpose of
+ // making sure dequeueBuffer() won't block.
+ // i.e.: was the BufferQueue in "mDequeueBufferCannotBlock" when this buffer
+ // was queued.
+ bool mIsDroppable;
+
+ // Indicates whether this buffer has been seen by a consumer yet
+ bool mAcquireCalled;
+
+ // Indicates this buffer must be transformed by the inverse transform of the screen
+ // it is displayed onto. This is applied after mTransform.
+ bool mTransformToDisplayInverse;
+ };
+
+
+ // acquireBuffer attempts to acquire ownership of the next pending buffer in
+ // the BufferQueue. If no buffer is pending then it returns -EINVAL. If a
+ // buffer is successfully acquired, the information about the buffer is
+ // returned in BufferItem. If the buffer returned had previously been
+ // acquired then the BufferItem::mGraphicBuffer field of buffer is set to
+ // NULL and it is assumed that the consumer still holds a reference to the
+ // buffer.
+ //
+ // If presentWhen is nonzero, it indicates the time when the buffer will
+ // be displayed on screen. If the buffer's timestamp is farther in the
+ // future, the buffer won't be acquired, and PRESENT_LATER will be
+ // returned. The presentation time is in nanoseconds, and the time base
+ // is CLOCK_MONOTONIC.
+ virtual status_t acquireBuffer(BufferItem *buffer, nsecs_t presentWhen) = 0;
+
+ // releaseBuffer releases a buffer slot from the consumer back to the
+ // BufferQueue. This may be done while the buffer's contents are still
+ // being accessed. The fence will signal when the buffer is no longer
+ // in use. frameNumber is used to indentify the exact buffer returned.
+ //
+ // If releaseBuffer returns STALE_BUFFER_SLOT, then the consumer must free
+ // any references to the just-released buffer that it might have, as if it
+ // had received a onBuffersReleased() call with a mask set for the released
+ // buffer.
+ //
+ // Note that the dependencies on EGL will be removed once we switch to using
+ // the Android HW Sync HAL.
+ virtual status_t releaseBuffer(int buf, uint64_t frameNumber, const sp<Fence>& releaseFence) = 0;
+
+ // consumerConnect connects a consumer to the BufferQueue. Only one
+ // consumer may be connected, and when that consumer disconnects the
+ // BufferQueue is placed into the "abandoned" state, causing most
+ // interactions with the BufferQueue by the producer to fail.
+ // controlledByApp indicates whether the consumer is controlled by
+ // the application.
+ //
+ // consumer may not be NULL.
+ virtual status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp) = 0;
+
+ // consumerDisconnect disconnects a consumer from the BufferQueue. All
+ // buffers will be freed and the BufferQueue is placed in the "abandoned"
+ // state, causing most interactions with the BufferQueue by the producer to
+ // fail.
+ virtual status_t consumerDisconnect() = 0;
+
+ // getReleasedBuffers sets the value pointed to by slotMask to a bit mask
+ // indicating which buffer slots have been released by the BufferQueue
+ // but have not yet been released by the consumer.
+ //
+ // This should be called from the onBuffersReleased() callback.
+ virtual status_t getReleasedBuffers(uint32_t* slotMask) = 0;
+
+ // setDefaultBufferSize is used to set the size of buffers returned by
+ // dequeueBuffer when a width and height of zero is requested. Default
+ // is 1x1.
+ virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h) = 0;
+
+ // setDefaultMaxBufferCount sets the default value for the maximum buffer
+ // count (the initial default is 2). If the producer has requested a
+ // buffer count using setBufferCount, the default buffer count will only
+ // take effect if the producer sets the count back to zero.
+ //
+ // The count must be between 2 and NUM_BUFFER_SLOTS, inclusive.
+ virtual status_t setDefaultMaxBufferCount(int bufferCount) = 0;
+
+ // disableAsyncBuffer disables the extra buffer used in async mode
+ // (when both producer and consumer have set their "isControlledByApp"
+ // flag) and has dequeueBuffer() return WOULD_BLOCK instead.
+ //
+ // This can only be called before consumerConnect().
+ virtual status_t disableAsyncBuffer() = 0;
+
+ // setMaxAcquiredBufferCount sets the maximum number of buffers that can
+ // be acquired by the consumer at one time (default 1). This call will
+ // fail if a producer is connected to the BufferQueue.
+ virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) = 0;
+
+ // setConsumerName sets the name used in logging
+ virtual void setConsumerName(const String8& name) = 0;
+
+ // setDefaultBufferFormat allows the BufferQueue to create
+ // GraphicBuffers of a defaultFormat if no format is specified
+ // in dequeueBuffer. Formats are enumerated in graphics.h; the
+ // initial default is HAL_PIXEL_FORMAT_RGBA_8888.
+ virtual status_t setDefaultBufferFormat(uint32_t defaultFormat) = 0;
+
+ // setConsumerUsageBits will turn on additional usage bits for dequeueBuffer.
+ // These are merged with the bits passed to dequeueBuffer. The values are
+ // enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER; the default is 0.
+ virtual status_t setConsumerUsageBits(uint32_t usage) = 0;
+
+ // setTransformHint bakes in rotation to buffers so overlays can be used.
+ // The values are enumerated in window.h, e.g.
+ // NATIVE_WINDOW_TRANSFORM_ROT_90. The default is 0 (no transform).
+ virtual status_t setTransformHint(uint32_t hint) = 0;
+
+ // dump state into a string
+ virtual void dumpToString(String8& result, const char* prefix) const = 0;
+
+public:
+ DECLARE_META_INTERFACE(GonkGraphicBufferConsumer);
+};
+
+// ----------------------------------------------------------------------------
+
+class BnGonkGraphicBufferConsumer : public BnInterface<IGonkGraphicBufferConsumer>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_GUI_IGRAPHICBUFFERCONSUMER_H
diff --git a/widget/gonk/nativewindow/IGonkGraphicBufferConsumerLL.cpp b/widget/gonk/nativewindow/IGonkGraphicBufferConsumerLL.cpp
new file mode 100644
index 000000000..729ed6736
--- /dev/null
+++ b/widget/gonk/nativewindow/IGonkGraphicBufferConsumerLL.cpp
@@ -0,0 +1,565 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/NativeHandle.h>
+
+#include <binder/Parcel.h>
+#include <binder/IInterface.h>
+
+#include <gui/IConsumerListener.h>
+#include "IGonkGraphicBufferConsumerLL.h"
+
+#include <ui/GraphicBuffer.h>
+#include <ui/Fence.h>
+
+#include <system/window.h>
+
+#include "mozilla/layers/TextureClient.h"
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+IGonkGraphicBufferConsumer::BufferItem::BufferItem() :
+ mTransform(0),
+ mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+ mTimestamp(0),
+ mIsAutoTimestamp(false),
+ mFrameNumber(0),
+ mBuf(INVALID_BUFFER_SLOT),
+ mIsDroppable(false),
+ mAcquireCalled(false),
+ mTransformToDisplayInverse(false) {
+ mCrop.makeInvalid();
+}
+
+size_t IGonkGraphicBufferConsumer::BufferItem::getPodSize() const {
+ size_t c = sizeof(mCrop) +
+ sizeof(mTransform) +
+ sizeof(mScalingMode) +
+ sizeof(mTimestamp) +
+ sizeof(mIsAutoTimestamp) +
+ sizeof(mFrameNumber) +
+ sizeof(mBuf) +
+ sizeof(mIsDroppable) +
+ sizeof(mAcquireCalled) +
+ sizeof(mTransformToDisplayInverse);
+ return c;
+}
+
+size_t IGonkGraphicBufferConsumer::BufferItem::getFlattenedSize() const {
+ size_t c = 0;
+ if (mGraphicBuffer != 0) {
+ c += mGraphicBuffer->getFlattenedSize();
+ c = FlattenableUtils::align<4>(c);
+ }
+ if (mFence != 0) {
+ c += mFence->getFlattenedSize();
+ c = FlattenableUtils::align<4>(c);
+ }
+ return sizeof(int32_t) + c + getPodSize();
+}
+
+size_t IGonkGraphicBufferConsumer::BufferItem::getFdCount() const {
+ size_t c = 0;
+ if (mGraphicBuffer != 0) {
+ c += mGraphicBuffer->getFdCount();
+ }
+ if (mFence != 0) {
+ c += mFence->getFdCount();
+ }
+ return c;
+}
+
+static void writeBoolAsInt(void*& buffer, size_t& size, bool b) {
+ FlattenableUtils::write(buffer, size, static_cast<int32_t>(b));
+}
+
+static bool readBoolFromInt(void const*& buffer, size_t& size) {
+ int32_t i;
+ FlattenableUtils::read(buffer, size, i);
+ return static_cast<bool>(i);
+}
+
+status_t IGonkGraphicBufferConsumer::BufferItem::flatten(
+ void*& buffer, size_t& size, int*& fds, size_t& count) const {
+
+ // make sure we have enough space
+ if (size < BufferItem::getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ // content flags are stored first
+ uint32_t& flags = *static_cast<uint32_t*>(buffer);
+
+ // advance the pointer
+ FlattenableUtils::advance(buffer, size, sizeof(uint32_t));
+
+ flags = 0;
+ if (mGraphicBuffer != 0) {
+ status_t err = mGraphicBuffer->flatten(buffer, size, fds, count);
+ if (err) return err;
+ size -= FlattenableUtils::align<4>(buffer);
+ flags |= 1;
+ }
+ if (mFence != 0) {
+ status_t err = mFence->flatten(buffer, size, fds, count);
+ if (err) return err;
+ size -= FlattenableUtils::align<4>(buffer);
+ flags |= 2;
+ }
+
+ // check we have enough space (in case flattening the fence/graphicbuffer lied to us)
+ if (size < getPodSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(buffer, size, mCrop);
+ FlattenableUtils::write(buffer, size, mTransform);
+ FlattenableUtils::write(buffer, size, mScalingMode);
+ FlattenableUtils::write(buffer, size, mTimestamp);
+ writeBoolAsInt(buffer, size, mIsAutoTimestamp);
+ FlattenableUtils::write(buffer, size, mFrameNumber);
+ FlattenableUtils::write(buffer, size, mBuf);
+ writeBoolAsInt(buffer, size, mIsDroppable);
+ writeBoolAsInt(buffer, size, mAcquireCalled);
+ writeBoolAsInt(buffer, size, mTransformToDisplayInverse);
+
+ return NO_ERROR;
+}
+
+status_t IGonkGraphicBufferConsumer::BufferItem::unflatten(
+ void const*& buffer, size_t& size, int const*& fds, size_t& count) {
+
+ if (size < sizeof(uint32_t))
+ return NO_MEMORY;
+
+ uint32_t flags = 0;
+ FlattenableUtils::read(buffer, size, flags);
+
+ if (flags & 1) {
+ mGraphicBuffer = new GraphicBuffer();
+ status_t err = mGraphicBuffer->unflatten(buffer, size, fds, count);
+ if (err) return err;
+ size -= FlattenableUtils::align<4>(buffer);
+ }
+
+ if (flags & 2) {
+ mFence = new Fence();
+ status_t err = mFence->unflatten(buffer, size, fds, count);
+ if (err) return err;
+ size -= FlattenableUtils::align<4>(buffer);
+ }
+
+ // check we have enough space
+ if (size < getPodSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, mCrop);
+ FlattenableUtils::read(buffer, size, mTransform);
+ FlattenableUtils::read(buffer, size, mScalingMode);
+ FlattenableUtils::read(buffer, size, mTimestamp);
+ mIsAutoTimestamp = readBoolFromInt(buffer, size);
+ FlattenableUtils::read(buffer, size, mFrameNumber);
+ FlattenableUtils::read(buffer, size, mBuf);
+ mIsDroppable = readBoolFromInt(buffer, size);
+ mAcquireCalled = readBoolFromInt(buffer, size);
+ mTransformToDisplayInverse = readBoolFromInt(buffer, size);
+
+ return NO_ERROR;
+}
+
+// ---------------------------------------------------------------------------
+
+enum {
+ ACQUIRE_BUFFER = IBinder::FIRST_CALL_TRANSACTION,
+ DETACH_BUFFER,
+ ATTACH_BUFFER,
+ RELEASE_BUFFER,
+ CONSUMER_CONNECT,
+ CONSUMER_DISCONNECT,
+ GET_RELEASED_BUFFERS,
+ SET_DEFAULT_BUFFER_SIZE,
+ SET_DEFAULT_MAX_BUFFER_COUNT,
+ DISABLE_ASYNC_BUFFER,
+ SET_MAX_ACQUIRED_BUFFER_COUNT,
+ SET_CONSUMER_NAME,
+ SET_DEFAULT_BUFFER_FORMAT,
+ SET_CONSUMER_USAGE_BITS,
+ SET_TRANSFORM_HINT,
+ GET_SIDEBAND_STREAM,
+ DUMP,
+};
+
+
+class BpGonkGraphicBufferConsumer : public BpInterface<IGonkGraphicBufferConsumer>
+{
+public:
+ BpGonkGraphicBufferConsumer(const sp<IBinder>& impl)
+ : BpInterface<IGonkGraphicBufferConsumer>(impl)
+ {
+ }
+
+ virtual status_t acquireBuffer(BufferItem *buffer, nsecs_t presentWhen) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor());
+ data.writeInt64(presentWhen);
+ status_t result = remote()->transact(ACQUIRE_BUFFER, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.read(*buffer);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ return reply.readInt32();
+ }
+
+ virtual status_t detachBuffer(int slot) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor());
+ data.writeInt32(slot);
+ status_t result = remote()->transact(DETACH_BUFFER, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.readInt32();
+ return result;
+ }
+
+ virtual status_t attachBuffer(int* slot, const sp<GraphicBuffer>& buffer) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor());
+ data.write(*buffer.get());
+ status_t result = remote()->transact(ATTACH_BUFFER, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ *slot = reply.readInt32();
+ result = reply.readInt32();
+ return result;
+ }
+
+ virtual status_t releaseBuffer(int buf, uint64_t frameNumber, const sp<Fence>& releaseFence) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor());
+ data.writeInt32(buf);
+ data.writeInt64(frameNumber);
+ data.write(*releaseFence);
+ status_t result = remote()->transact(RELEASE_BUFFER, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ return reply.readInt32();
+ }
+
+ virtual status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor());
+ data.writeStrongBinder(consumer->asBinder());
+ data.writeInt32(controlledByApp);
+ status_t result = remote()->transact(CONSUMER_CONNECT, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ return reply.readInt32();
+ }
+
+ virtual status_t consumerDisconnect() {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor());
+ status_t result = remote()->transact(CONSUMER_DISCONNECT, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ return reply.readInt32();
+ }
+
+ virtual status_t getReleasedBuffers(uint64_t* slotMask) {
+ Parcel data, reply;
+ if (slotMask == NULL) {
+ ALOGE("getReleasedBuffers: slotMask must not be NULL");
+ return BAD_VALUE;
+ }
+ data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor());
+ status_t result = remote()->transact(GET_RELEASED_BUFFERS, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ *slotMask = reply.readInt64();
+ return reply.readInt32();
+ }
+
+ virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor());
+ data.writeInt32(w);
+ data.writeInt32(h);
+ status_t result = remote()->transact(SET_DEFAULT_BUFFER_SIZE, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ return reply.readInt32();
+ }
+
+ virtual status_t setDefaultMaxBufferCount(int bufferCount) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor());
+ data.writeInt32(bufferCount);
+ status_t result = remote()->transact(SET_DEFAULT_MAX_BUFFER_COUNT, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ return reply.readInt32();
+ }
+
+ virtual status_t disableAsyncBuffer() {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor());
+ status_t result = remote()->transact(DISABLE_ASYNC_BUFFER, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ return reply.readInt32();
+ }
+
+ virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor());
+ data.writeInt32(maxAcquiredBuffers);
+ status_t result = remote()->transact(SET_MAX_ACQUIRED_BUFFER_COUNT, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ return reply.readInt32();
+ }
+
+ virtual void setConsumerName(const String8& name) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor());
+ data.writeString8(name);
+ remote()->transact(SET_CONSUMER_NAME, data, &reply);
+ }
+
+ virtual status_t setDefaultBufferFormat(uint32_t defaultFormat) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor());
+ data.writeInt32(defaultFormat);
+ status_t result = remote()->transact(SET_DEFAULT_BUFFER_FORMAT, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ return reply.readInt32();
+ }
+
+ virtual status_t setConsumerUsageBits(uint32_t usage) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor());
+ data.writeInt32(usage);
+ status_t result = remote()->transact(SET_CONSUMER_USAGE_BITS, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ return reply.readInt32();
+ }
+
+ virtual status_t setTransformHint(uint32_t hint) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor());
+ data.writeInt32(hint);
+ status_t result = remote()->transact(SET_TRANSFORM_HINT, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ return reply.readInt32();
+ }
+
+ virtual sp<NativeHandle> getSidebandStream() const {
+ Parcel data, reply;
+ status_t err;
+ data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor());
+ if ((err = remote()->transact(GET_SIDEBAND_STREAM, data, &reply)) != NO_ERROR) {
+ return NULL;
+ }
+ sp<NativeHandle> stream;
+ if (reply.readInt32()) {
+ stream = NativeHandle::create(reply.readNativeHandle(), true);
+ }
+ return stream;
+ }
+
+ virtual void dumpToString(String8& result, const char* prefix) const {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor());
+ data.writeString8(result);
+ data.writeString8(String8(prefix ? prefix : ""));
+ remote()->transact(DUMP, data, &reply);
+ reply.readString8();
+ }
+
+ // Added by mozilla
+ virtual already_AddRefed<mozilla::layers::TextureClient>
+ getTextureClientFromBuffer(ANativeWindowBuffer* buffer)
+ {
+ return nullptr;
+ }
+
+ virtual int
+ getSlotFromTextureClientLocked(mozilla::layers::TextureClient* client) const
+ {
+ return BAD_VALUE;
+ }
+};
+
+IMPLEMENT_META_INTERFACE(GonkGraphicBufferConsumer, "android.gui.IGonkGraphicBufferConsumer");
+
+// ----------------------------------------------------------------------
+
+status_t BnGonkGraphicBufferConsumer::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch(code) {
+ case ACQUIRE_BUFFER: {
+ CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply);
+ BufferItem item;
+ int64_t presentWhen = data.readInt64();
+ status_t result = acquireBuffer(&item, presentWhen);
+ status_t err = reply->write(item);
+ if (err) return err;
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
+ case DETACH_BUFFER: {
+ CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply);
+ int slot = data.readInt32();
+ int result = detachBuffer(slot);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
+ case ATTACH_BUFFER: {
+ CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply);
+ sp<GraphicBuffer> buffer = new GraphicBuffer();
+ data.read(*buffer.get());
+ int slot;
+ int result = attachBuffer(&slot, buffer);
+ reply->writeInt32(slot);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
+ case RELEASE_BUFFER: {
+ CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply);
+ int buf = data.readInt32();
+ uint64_t frameNumber = data.readInt64();
+ sp<Fence> releaseFence = new Fence();
+ status_t err = data.read(*releaseFence);
+ if (err) return err;
+ status_t result = releaseBuffer(buf, frameNumber, releaseFence);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
+ case CONSUMER_CONNECT: {
+ CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply);
+ sp<IConsumerListener> consumer = IConsumerListener::asInterface( data.readStrongBinder() );
+ bool controlledByApp = data.readInt32();
+ status_t result = consumerConnect(consumer, controlledByApp);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
+ case CONSUMER_DISCONNECT: {
+ CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply);
+ status_t result = consumerDisconnect();
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
+ case GET_RELEASED_BUFFERS: {
+ CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply);
+ uint64_t slotMask;
+ status_t result = getReleasedBuffers(&slotMask);
+ reply->writeInt64(slotMask);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
+ case SET_DEFAULT_BUFFER_SIZE: {
+ CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply);
+ uint32_t w = data.readInt32();
+ uint32_t h = data.readInt32();
+ status_t result = setDefaultBufferSize(w, h);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
+ case SET_DEFAULT_MAX_BUFFER_COUNT: {
+ CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply);
+ uint32_t bufferCount = data.readInt32();
+ status_t result = setDefaultMaxBufferCount(bufferCount);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
+ case DISABLE_ASYNC_BUFFER: {
+ CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply);
+ status_t result = disableAsyncBuffer();
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
+ case SET_MAX_ACQUIRED_BUFFER_COUNT: {
+ CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply);
+ uint32_t maxAcquiredBuffers = data.readInt32();
+ status_t result = setMaxAcquiredBufferCount(maxAcquiredBuffers);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
+ case SET_CONSUMER_NAME: {
+ CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply);
+ setConsumerName( data.readString8() );
+ return NO_ERROR;
+ } break;
+ case SET_DEFAULT_BUFFER_FORMAT: {
+ CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply);
+ uint32_t defaultFormat = data.readInt32();
+ status_t result = setDefaultBufferFormat(defaultFormat);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
+ case SET_CONSUMER_USAGE_BITS: {
+ CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply);
+ uint32_t usage = data.readInt32();
+ status_t result = setConsumerUsageBits(usage);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
+ case SET_TRANSFORM_HINT: {
+ CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply);
+ uint32_t hint = data.readInt32();
+ status_t result = setTransformHint(hint);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
+ case DUMP: {
+ CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply);
+ String8 result = data.readString8();
+ String8 prefix = data.readString8();
+ static_cast<IGonkGraphicBufferConsumer*>(this)->dumpToString(result, prefix);
+ reply->writeString8(result);
+ return NO_ERROR;
+ }
+ }
+ return BBinder::onTransact(code, data, reply, flags);
+}
+
+}; // namespace android
diff --git a/widget/gonk/nativewindow/IGonkGraphicBufferConsumerLL.h b/widget/gonk/nativewindow/IGonkGraphicBufferConsumerLL.h
new file mode 100644
index 000000000..8a93a0849
--- /dev/null
+++ b/widget/gonk/nativewindow/IGonkGraphicBufferConsumerLL.h
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 2013 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 NATIVEWINDOW_IGONKGRAPHICBUFFERCONSUMER_LL_H
+#define NATIVEWINDOW_IGONKGRAPHICBUFFERCONSUMER_LL_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/Timers.h>
+
+#include <binder/IInterface.h>
+#include <ui/Rect.h>
+
+#include "mozilla/RefPtr.h"
+
+class ANativeWindowBuffer;
+
+namespace mozilla {
+namespace layers {
+class TextureClient;
+}
+}
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+class Fence;
+class GraphicBuffer;
+class IConsumerListener;
+class NativeHandle;
+
+class IGonkGraphicBufferConsumer : public IInterface {
+public:
+
+ // public facing structure for BufferSlot
+ class BufferItem : public Flattenable<BufferItem> {
+ friend class Flattenable<BufferItem>;
+ size_t getPodSize() const;
+ size_t getFlattenedSize() const;
+ size_t getFdCount() const;
+ status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
+ status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
+
+ public:
+ // The default value of mBuf, used to indicate this doesn't correspond to a slot.
+ enum { INVALID_BUFFER_SLOT = -1 };
+ BufferItem();
+
+ // mGraphicBuffer points to the buffer allocated for this slot, or is NULL
+ // if the buffer in this slot has been acquired in the past (see
+ // BufferSlot.mAcquireCalled).
+ sp<GraphicBuffer> mGraphicBuffer;
+
+ // mFence is a fence that will signal when the buffer is idle.
+ sp<Fence> mFence;
+
+ // mCrop is the current crop rectangle for this buffer slot.
+ Rect mCrop;
+
+ // mTransform is the current transform flags for this buffer slot.
+ // refer to NATIVE_WINDOW_TRANSFORM_* in <window.h>
+ uint32_t mTransform;
+
+ // mScalingMode is the current scaling mode for this buffer slot.
+ // refer to NATIVE_WINDOW_SCALING_* in <window.h>
+ uint32_t mScalingMode;
+
+ // mTimestamp is the current timestamp for this buffer slot. This gets
+ // to set by queueBuffer each time this slot is queued. This value
+ // is guaranteed to be monotonically increasing for each newly
+ // acquired buffer.
+ int64_t mTimestamp;
+
+ // mIsAutoTimestamp indicates whether mTimestamp was generated
+ // automatically when the buffer was queued.
+ bool mIsAutoTimestamp;
+
+ // mFrameNumber is the number of the queued frame for this slot.
+ uint64_t mFrameNumber;
+
+ // mBuf is the slot index of this buffer (default INVALID_BUFFER_SLOT).
+ int mBuf;
+
+ // mIsDroppable whether this buffer was queued with the
+ // property that it can be replaced by a new buffer for the purpose of
+ // making sure dequeueBuffer() won't block.
+ // i.e.: was the BufferQueue in "mDequeueBufferCannotBlock" when this buffer
+ // was queued.
+ bool mIsDroppable;
+
+ // Indicates whether this buffer has been seen by a consumer yet
+ bool mAcquireCalled;
+
+ // Indicates this buffer must be transformed by the inverse transform of the screen
+ // it is displayed onto. This is applied after mTransform.
+ bool mTransformToDisplayInverse;
+ };
+
+ enum {
+ // Returned by releaseBuffer, after which the consumer must
+ // free any references to the just-released buffer that it might have.
+ STALE_BUFFER_SLOT = 1,
+ // Returned by dequeueBuffer if there are no pending buffers available.
+ NO_BUFFER_AVAILABLE,
+ // Returned by dequeueBuffer if it's too early for the buffer to be acquired.
+ PRESENT_LATER,
+ };
+
+ // acquireBuffer attempts to acquire ownership of the next pending buffer in
+ // the BufferQueue. If no buffer is pending then it returns
+ // NO_BUFFER_AVAILABLE. If a buffer is successfully acquired, the
+ // information about the buffer is returned in BufferItem.
+ //
+ // If the buffer returned had previously been
+ // acquired then the BufferItem::mGraphicBuffer field of buffer is set to
+ // NULL and it is assumed that the consumer still holds a reference to the
+ // buffer.
+ //
+ // If presentWhen is non-zero, it indicates the time when the buffer will
+ // be displayed on screen. If the buffer's timestamp is farther in the
+ // future, the buffer won't be acquired, and PRESENT_LATER will be
+ // returned. The presentation time is in nanoseconds, and the time base
+ // is CLOCK_MONOTONIC.
+ //
+ // Return of NO_ERROR means the operation completed as normal.
+ //
+ // Return of a positive value means the operation could not be completed
+ // at this time, but the user should try again later:
+ // * NO_BUFFER_AVAILABLE - no buffer is pending (nothing queued by producer)
+ // * PRESENT_LATER - the buffer's timestamp is farther in the future
+ //
+ // Return of a negative value means an error has occurred:
+ // * INVALID_OPERATION - too many buffers have been acquired
+ virtual status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen) = 0;
+
+ // detachBuffer attempts to remove all ownership of the buffer in the given
+ // slot from the buffer queue. If this call succeeds, the slot will be
+ // freed, and there will be no way to obtain the buffer from this interface.
+ // The freed slot will remain unallocated until either it is selected to
+ // hold a freshly allocated buffer in dequeueBuffer or a buffer is attached
+ // to the slot. The buffer must have already been acquired.
+ //
+ // Return of a value other than NO_ERROR means an error has occurred:
+ // * BAD_VALUE - the given slot number is invalid, either because it is
+ // out of the range [0, NUM_BUFFER_SLOTS) or because the slot
+ // it refers to is not currently acquired.
+ virtual status_t detachBuffer(int slot) = 0;
+
+ // attachBuffer attempts to transfer ownership of a buffer to the buffer
+ // queue. If this call succeeds, it will be as if this buffer was acquired
+ // from the returned slot number. As such, this call will fail if attaching
+ // this buffer would cause too many buffers to be simultaneously acquired.
+ //
+ // If the buffer is successfully attached, its frameNumber is initialized
+ // to 0. This must be passed into the releaseBuffer call or else the buffer
+ // will be deallocated as stale.
+ //
+ // Return of a value other than NO_ERROR means an error has occurred:
+ // * BAD_VALUE - outSlot or buffer were NULL
+ // * INVALID_OPERATION - cannot attach the buffer because it would cause too
+ // many buffers to be acquired.
+ // * NO_MEMORY - no free slots available
+ virtual status_t attachBuffer(int *outSlot,
+ const sp<GraphicBuffer>& buffer) = 0;
+
+ // releaseBuffer releases a buffer slot from the consumer back to the
+ // BufferQueue. This may be done while the buffer's contents are still
+ // being accessed. The fence will signal when the buffer is no longer
+ // in use. frameNumber is used to indentify the exact buffer returned.
+ //
+ // If releaseBuffer returns STALE_BUFFER_SLOT, then the consumer must free
+ // any references to the just-released buffer that it might have, as if it
+ // had received a onBuffersReleased() call with a mask set for the released
+ // buffer.
+ //
+ // Note that the dependencies on EGL will be removed once we switch to using
+ // the Android HW Sync HAL.
+ //
+ // Return of NO_ERROR means the operation completed as normal.
+ //
+ // Return of a positive value means the operation could not be completed
+ // at this time, but the user should try again later:
+ // * STALE_BUFFER_SLOT - see above (second paragraph)
+ //
+ // Return of a negative value means an error has occurred:
+ // * BAD_VALUE - one of the following could've happened:
+ // * the buffer slot was invalid
+ // * the fence was NULL
+ // * the buffer slot specified is not in the acquired state
+ virtual status_t releaseBuffer(int buf, uint64_t frameNumber, const sp<Fence>& releaseFence) = 0;
+
+ // consumerConnect connects a consumer to the BufferQueue. Only one
+ // consumer may be connected, and when that consumer disconnects the
+ // BufferQueue is placed into the "abandoned" state, causing most
+ // interactions with the BufferQueue by the producer to fail.
+ // controlledByApp indicates whether the consumer is controlled by
+ // the application.
+ //
+ // consumer may not be NULL.
+ //
+ // Return of a value other than NO_ERROR means an error has occurred:
+ // * NO_INIT - the buffer queue has been abandoned
+ // * BAD_VALUE - a NULL consumer was provided
+ virtual status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp) = 0;
+
+ // consumerDisconnect disconnects a consumer from the BufferQueue. All
+ // buffers will be freed and the BufferQueue is placed in the "abandoned"
+ // state, causing most interactions with the BufferQueue by the producer to
+ // fail.
+ //
+ // Return of a value other than NO_ERROR means an error has occurred:
+ // * BAD_VALUE - no consumer is currently connected
+ virtual status_t consumerDisconnect() = 0;
+
+ // getReleasedBuffers sets the value pointed to by slotMask to a bit set.
+ // Each bit index with a 1 corresponds to a released buffer slot with that
+ // index value. In particular, a released buffer is one that has
+ // been released by the BufferQueue but have not yet been released by the consumer.
+ //
+ // This should be called from the onBuffersReleased() callback.
+ //
+ // Return of a value other than NO_ERROR means an error has occurred:
+ // * NO_INIT - the buffer queue has been abandoned.
+ virtual status_t getReleasedBuffers(uint64_t* slotMask) = 0;
+
+ // setDefaultBufferSize is used to set the size of buffers returned by
+ // dequeueBuffer when a width and height of zero is requested. Default
+ // is 1x1.
+ //
+ // Return of a value other than NO_ERROR means an error has occurred:
+ // * BAD_VALUE - either w or h was zero
+ virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h) = 0;
+
+ // setDefaultMaxBufferCount sets the default value for the maximum buffer
+ // count (the initial default is 2). If the producer has requested a
+ // buffer count using setBufferCount, the default buffer count will only
+ // take effect if the producer sets the count back to zero.
+ //
+ // The count must be between 2 and NUM_BUFFER_SLOTS, inclusive.
+ //
+ // Return of a value other than NO_ERROR means an error has occurred:
+ // * BAD_VALUE - bufferCount was out of range (see above).
+ virtual status_t setDefaultMaxBufferCount(int bufferCount) = 0;
+
+ // disableAsyncBuffer disables the extra buffer used in async mode
+ // (when both producer and consumer have set their "isControlledByApp"
+ // flag) and has dequeueBuffer() return WOULD_BLOCK instead.
+ //
+ // This can only be called before consumerConnect().
+ //
+ // Return of a value other than NO_ERROR means an error has occurred:
+ // * INVALID_OPERATION - attempting to call this after consumerConnect.
+ virtual status_t disableAsyncBuffer() = 0;
+
+ // setMaxAcquiredBufferCount sets the maximum number of buffers that can
+ // be acquired by the consumer at one time (default 1). This call will
+ // fail if a producer is connected to the BufferQueue.
+ //
+ // maxAcquiredBuffers must be (inclusive) between 1 and MAX_MAX_ACQUIRED_BUFFERS.
+ //
+ // Return of a value other than NO_ERROR means an error has occurred:
+ // * BAD_VALUE - maxAcquiredBuffers was out of range (see above).
+ // * INVALID_OPERATION - attempting to call this after a producer connected.
+ virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) = 0;
+
+ // setConsumerName sets the name used in logging
+ virtual void setConsumerName(const String8& name) = 0;
+
+ // setDefaultBufferFormat allows the BufferQueue to create
+ // GraphicBuffers of a defaultFormat if no format is specified
+ // in dequeueBuffer. Formats are enumerated in graphics.h; the
+ // initial default is HAL_PIXEL_FORMAT_RGBA_8888.
+ //
+ // Return of a value other than NO_ERROR means an unknown error has occurred.
+ virtual status_t setDefaultBufferFormat(uint32_t defaultFormat) = 0;
+
+ // setConsumerUsageBits will turn on additional usage bits for dequeueBuffer.
+ // These are merged with the bits passed to dequeueBuffer. The values are
+ // enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER; the default is 0.
+ //
+ // Return of a value other than NO_ERROR means an unknown error has occurred.
+ virtual status_t setConsumerUsageBits(uint32_t usage) = 0;
+
+ // setTransformHint bakes in rotation to buffers so overlays can be used.
+ // The values are enumerated in window.h, e.g.
+ // NATIVE_WINDOW_TRANSFORM_ROT_90. The default is 0 (no transform).
+ //
+ // Return of a value other than NO_ERROR means an unknown error has occurred.
+ virtual status_t setTransformHint(uint32_t hint) = 0;
+
+ // Retrieve the sideband buffer stream, if any.
+ virtual sp<NativeHandle> getSidebandStream() const = 0;
+
+ // dump state into a string
+ virtual void dumpToString(String8& result, const char* prefix) const = 0;
+
+ // Added by mozilla
+ virtual already_AddRefed<mozilla::layers::TextureClient>
+ getTextureClientFromBuffer(ANativeWindowBuffer* buffer) = 0;
+
+ virtual int getSlotFromTextureClientLocked(mozilla::layers::TextureClient* client) const = 0;
+
+public:
+ DECLARE_META_INTERFACE(GonkGraphicBufferConsumer);
+};
+
+// ----------------------------------------------------------------------------
+
+class BnGonkGraphicBufferConsumer : public BnInterface<IGonkGraphicBufferConsumer>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_GUI_IGONKGRAPHICBUFFERCONSUMER_H
diff --git a/widget/gonk/nativewindow/moz.build b/widget/gonk/nativewindow/moz.build
new file mode 100644
index 000000000..fbcee601c
--- /dev/null
+++ b/widget/gonk/nativewindow/moz.build
@@ -0,0 +1,104 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# Copyright 2013 Mozilla Foundation and Mozilla contributors
+#
+# 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.
+
+EXPORTS += [
+ 'GonkBufferQueue.h',
+ 'GonkNativeWindow.h',
+]
+
+if CONFIG['ANDROID_VERSION'] >= '19':
+ EXPORTS += [
+ 'IGonkGraphicBufferConsumer.h',
+ ]
+
+if CONFIG['ANDROID_VERSION'] >= '21':
+ EXPORTS += [
+ 'GonkBufferQueueLL/GonkBufferQueueDefs.h',
+ 'GonkBufferQueueLL/GonkBufferQueueLL.h',
+ 'GonkBufferQueueLL/GonkBufferQueueProducer.h',
+ 'GonkBufferQueueLL/GonkBufferSlot.h',
+ 'GonkConsumerBaseLL.h',
+ 'GonkNativeWindowLL.h',
+ 'IGonkGraphicBufferConsumerLL.h',
+ ]
+elif CONFIG['ANDROID_VERSION'] >= '19':
+ EXPORTS += [
+ 'GonkBufferQueueKK.h',
+ 'GonkConsumerBaseKK.h',
+ 'GonkNativeWindowKK.h',
+ 'IGonkGraphicBufferConsumerKK.h',
+ ]
+elif CONFIG['ANDROID_VERSION'] in ('17', '18'):
+ EXPORTS += [
+ 'GonkBufferQueueJB.h',
+ 'GonkConsumerBaseJB.h',
+ 'GonkNativeWindowJB.h',
+ ]
+
+if CONFIG['MOZ_WEBRTC']:
+ if CONFIG['ANDROID_VERSION'] >= '21':
+ SOURCES += [
+ 'GonkBufferQueueLL/GonkBufferItem.cpp',
+ 'GonkBufferQueueLL/GonkBufferQueueConsumer.cpp',
+ 'GonkBufferQueueLL/GonkBufferQueueCore.cpp',
+ 'GonkBufferQueueLL/GonkBufferQueueLL.cpp',
+ 'GonkBufferQueueLL/GonkBufferQueueProducer.cpp',
+ 'GonkBufferQueueLL/GonkBufferSlot.cpp',
+ 'GonkConsumerBaseLL.cpp',
+ 'GonkNativeWindowLL.cpp',
+ 'IGonkGraphicBufferConsumerLL.cpp',
+ ]
+ elif CONFIG['ANDROID_VERSION'] >= '19':
+ SOURCES += [
+ 'GonkBufferQueueKK.cpp',
+ 'GonkConsumerBaseKK.cpp',
+ 'GonkNativeWindowKK.cpp',
+ 'IGonkGraphicBufferConsumerKK.cpp',
+ ]
+ elif CONFIG['ANDROID_VERSION'] in ('17', '18'):
+ SOURCES += [
+ 'GonkBufferQueueJB.cpp',
+ 'GonkConsumerBaseJB.cpp',
+ 'GonkNativeWindowJB.cpp',
+ ]
+
+if CONFIG['ANDROID_VERSION'] >= '18':
+ SOURCES += [
+ 'FakeSurfaceComposer.cpp',
+ ]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+if CONFIG['ANDROID_VERSION'] >= '18':
+ LOCAL_INCLUDES += [
+ '%' + '%s/%s' % (CONFIG['ANDROID_SOURCE'], d) for d in [
+ 'frameworks/native/opengl/include',
+ ]
+ ]
+
+DEFINES['HAVE_ANDROID_OS'] = True
+
+# Suppress some GCC warnings being treated as errors:
+# - about attributes on forward declarations for types that are already
+# defined, which complains about an important MOZ_EXPORT for android::AString
+if CONFIG['GNU_CC']:
+ CXXFLAGS += ['-Wno-error=attributes', '-Wno-overloaded-virtual']
+
+FINAL_LIBRARY = 'xul'
+
+DISABLE_STL_WRAPPING = True
+
+NO_VISIBILITY_FLAGS = True