Merge "BufferQueue: Add allocateBuffers method"
diff --git a/include/gui/BufferQueueProducer.h b/include/gui/BufferQueueProducer.h
index 9df3633..fe8a308 100644
--- a/include/gui/BufferQueueProducer.h
+++ b/include/gui/BufferQueueProducer.h
@@ -169,6 +169,10 @@
     // 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);
+
 private:
     // This is required by the IBinder::DeathRecipient interface
     virtual void binderDied(const wp<IBinder>& who);
diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h
index d9e116b..9b96b2b 100644
--- a/include/gui/IGraphicBufferProducer.h
+++ b/include/gui/IGraphicBufferProducer.h
@@ -429,6 +429,19 @@
     // Passing NULL or a different stream handle will detach the previous
     // handle if any.
     virtual status_t setSidebandStream(const sp<NativeHandle>& stream) = 0;
+
+    // Allocates buffers based on the given dimensions/format.
+    //
+    // This function will allocate up to the maximum number of buffers
+    // permitted by the current BufferQueue configuration. It will use the
+    // given format, dimensions, and usage bits, which are interpreted in the
+    // same way as for dequeueBuffer, and the async flag must be set the same
+    // way as for dequeueBuffer to ensure that the correct number of buffers are
+    // allocated. This is most useful to avoid an allocation delay during
+    // dequeueBuffer. If there are already the maximum number of buffers
+    // allocated, this function has no effect.
+    virtual void allocateBuffers(bool async, uint32_t width, uint32_t height,
+            uint32_t format, uint32_t usage) = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/gui/Surface.h b/include/gui/Surface.h
index d8e9756..dcfe74f 100644
--- a/include/gui/Surface.h
+++ b/include/gui/Surface.h
@@ -91,6 +91,16 @@
      */
     void setSidebandStream(const sp<NativeHandle>& stream);
 
+    /* Allocates buffers based on the current dimensions/format.
+     *
+     * This function will allocate up to the maximum number of buffers
+     * permitted by the current BufferQueue configuration. It will use the
+     * default format and dimensions. This is most useful to avoid an allocation
+     * delay during dequeueBuffer. If there are already the maximum number of
+     * buffers allocated, this function has no effect.
+     */
+    void allocateBuffers();
+
 protected:
     virtual ~Surface();
 
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 7017ddf..70c3ff3 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -331,6 +331,7 @@
 
     if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
         status_t error;
+        BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot);
         sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer(
                     width, height, format, usage, &error));
         if (graphicBuffer == NULL) {
@@ -852,6 +853,60 @@
     return NO_ERROR;
 }
 
+void BufferQueueProducer::allocateBuffers(bool async, uint32_t width,
+        uint32_t height, uint32_t format, uint32_t usage) {
+    Vector<int> freeSlots;
+
+    Mutex::Autolock lock(mCore->mMutex);
+
+    int currentBufferCount = 0;
+    for (int slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
+        if (mSlots[slot].mGraphicBuffer != NULL) {
+            ++currentBufferCount;
+        } else {
+            if (mSlots[slot].mBufferState != BufferSlot::FREE) {
+                BQ_LOGE("allocateBuffers: slot %d without buffer is not FREE",
+                        slot);
+                continue;
+            }
+
+            freeSlots.push_front(slot);
+        }
+    }
+
+    int maxBufferCount = mCore->getMaxBufferCountLocked(async);
+    BQ_LOGV("allocateBuffers: allocating from %d buffers up to %d buffers",
+            currentBufferCount, maxBufferCount);
+    for (; currentBufferCount < maxBufferCount; ++currentBufferCount) {
+        if (freeSlots.empty()) {
+            BQ_LOGE("allocateBuffers: ran out of free slots");
+            return;
+        }
+
+        width = width > 0 ? width : mCore->mDefaultWidth;
+        height = height > 0 ? height : mCore->mDefaultHeight;
+        format = format != 0 ? format : mCore->mDefaultBufferFormat;
+        usage |= mCore->mConsumerUsageBits;
+
+        status_t result = NO_ERROR;
+        sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer(
+                width, height, format, usage, &result));
+        if (result != NO_ERROR) {
+            BQ_LOGE("allocateBuffers: failed to allocate buffer (%u x %u, format"
+                    " %u, usage %u)", width, height, format, usage);
+            return;
+        }
+
+        int slot = freeSlots[freeSlots.size() - 1];
+        mCore->freeBufferLocked(slot); // Clean up the slot first
+        mSlots[slot].mGraphicBuffer = graphicBuffer;
+        mSlots[slot].mFrameNumber = 0;
+        mSlots[slot].mFence = Fence::NO_FENCE;
+        BQ_LOGV("allocateBuffers: allocated a new buffer in slot %d", slot);
+        freeSlots.pop();
+    }
+}
+
 void BufferQueueProducer::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
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index aa6acb9..8d9a800 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -45,6 +45,7 @@
     CONNECT,
     DISCONNECT,
     SET_SIDEBAND_STREAM,
+    ALLOCATE_BUFFERS,
 };
 
 class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
@@ -252,6 +253,21 @@
         }
         return result;
     }
+
+    virtual void allocateBuffers(bool async, uint32_t width, uint32_t height,
+            uint32_t format, uint32_t usage) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+        data.writeInt32(static_cast<int32_t>(async));
+        data.writeInt32(static_cast<int32_t>(width));
+        data.writeInt32(static_cast<int32_t>(height));
+        data.writeInt32(static_cast<int32_t>(format));
+        data.writeInt32(static_cast<int32_t>(usage));
+        status_t result = remote()->transact(ALLOCATE_BUFFERS, data, &reply);
+        if (result != NO_ERROR) {
+            ALOGE("allocateBuffers failed to transact: %d", result);
+        }
+    }
 };
 
 IMPLEMENT_META_INTERFACE(GraphicBufferProducer, "android.gui.IGraphicBufferProducer");
@@ -394,6 +410,15 @@
             reply->writeInt32(result);
             return NO_ERROR;
         } break;
+        case ALLOCATE_BUFFERS:
+            CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+            bool async = static_cast<bool>(data.readInt32());
+            uint32_t width = static_cast<uint32_t>(data.readInt32());
+            uint32_t height = static_cast<uint32_t>(data.readInt32());
+            uint32_t format = static_cast<uint32_t>(data.readInt32());
+            uint32_t usage = static_cast<uint32_t>(data.readInt32());
+            allocateBuffers(async, width, height, format, usage);
+            return NO_ERROR;
     }
     return BBinder::onTransact(code, data, reply, flags);
 }
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 88c45b2..8cb9189 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -91,6 +91,13 @@
     mGraphicBufferProducer->setSidebandStream(stream);
 }
 
+void Surface::allocateBuffers() {
+    uint32_t reqWidth = mReqWidth ? mReqWidth : mUserWidth;
+    uint32_t reqHeight = mReqHeight ? mReqHeight : mUserHeight;
+    mGraphicBufferProducer->allocateBuffers(mSwapIntervalZero, mReqWidth,
+            mReqHeight, mReqFormat, mReqUsage);
+}
+
 int Surface::hook_setSwapInterval(ANativeWindow* window, int interval) {
     Surface* c = getSelf(window);
     return c->setSwapInterval(interval);
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index c415560..3442c65 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -486,6 +486,12 @@
     return INVALID_OPERATION;
 }
 
+void VirtualDisplaySurface::allocateBuffers(bool /* async */,
+        uint32_t /* width */, uint32_t /* height */, uint32_t /* format */,
+        uint32_t /* usage */) {
+    // TODO: Should we actually allocate buffers for a virtual display?
+}
+
 void VirtualDisplaySurface::updateQueueBufferOutput(
         const QueueBufferOutput& qbo) {
     uint32_t w, h, transformHint, numPendingBuffers;
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index 0ae9804..5c00ab4 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -112,6 +112,8 @@
             int api, bool producerControlledByApp, QueueBufferOutput* output);
     virtual status_t disconnect(int api);
     virtual status_t setSidebandStream(const sp<NativeHandle>& stream);
+    virtual void allocateBuffers(bool async, uint32_t width, uint32_t height,
+            uint32_t format, uint32_t usage);
 
     //
     // Utility methods
diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp
index d0e81bc..8739682 100644
--- a/services/surfaceflinger/MonitoredProducer.cpp
+++ b/services/surfaceflinger/MonitoredProducer.cpp
@@ -105,6 +105,11 @@
     return mProducer->setSidebandStream(stream);
 }
 
+void MonitoredProducer::allocateBuffers(bool async, uint32_t width,
+        uint32_t height, uint32_t format, uint32_t usage) {
+    mProducer->allocateBuffers(async, width, height, format, usage);
+}
+
 IBinder* MonitoredProducer::onAsBinder() {
     return mProducer->asBinder().get();
 }
diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h
index f034e39..f6ccc51 100644
--- a/services/surfaceflinger/MonitoredProducer.h
+++ b/services/surfaceflinger/MonitoredProducer.h
@@ -51,6 +51,8 @@
             bool producerControlledByApp, QueueBufferOutput* output);
     virtual status_t disconnect(int api);
     virtual status_t setSidebandStream(const sp<NativeHandle>& stream);
+    virtual void allocateBuffers(bool async, uint32_t width, uint32_t height,
+            uint32_t format, uint32_t usage);
     virtual IBinder* onAsBinder();
 
 private: