blast: Queue transactions by applying client

Do not update SurfaceFlinger until all the fences in a transaction
have signaled. While waiting on the fences, place the transaction
in a queue with other transactions that were applied by the
same SurfaceComposerClient.

Test: Transaction_test
Bug: 80477568

Change-Id: I6b866bfa955d5eafef28016a0c5de7c3862f1837
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 2d6be26..799151a 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -73,11 +73,9 @@
         return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder());
     }
 
-    virtual void setTransactionState(
-            const Vector<ComposerState>& state,
-            const Vector<DisplayState>& displays,
-            uint32_t flags)
-    {
+    virtual void setTransactionState(const Vector<ComposerState>& state,
+                                     const Vector<DisplayState>& displays, uint32_t flags,
+                                     const sp<IBinder>& applyToken) {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
 
@@ -92,6 +90,7 @@
         }
 
         data.writeUint32(flags);
+        data.writeStrongBinder(applyToken);
         remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply);
     }
 
@@ -750,7 +749,8 @@
             }
 
             uint32_t stateFlags = data.readUint32();
-            setTransactionState(state, displays, stateFlags);
+            sp<IBinder> applyToken = data.readStrongBinder();
+            setTransactionState(state, displays, stateFlags, applyToken);
             return NO_ERROR;
         }
         case BOOT_FINISHED: {
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 9586219..5b004e2 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -264,7 +264,9 @@
     mAnimation = false;
     mEarlyWakeup = false;
 
-    sf->setTransactionState(composerStates, displayStates, flags);
+    sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
+
+    sf->setTransactionState(composerStates, displayStates, flags, applyToken);
     mStatus = NO_ERROR;
     return NO_ERROR;
 }
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 3052c0b..8cb40e7 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -124,7 +124,8 @@
 
     /* open/close transactions. requires ACCESS_SURFACE_FLINGER permission */
     virtual void setTransactionState(const Vector<ComposerState>& state,
-            const Vector<DisplayState>& displays, uint32_t flags) = 0;
+                                     const Vector<DisplayState>& displays, uint32_t flags,
+                                     const sp<IBinder>& applyToken) = 0;
 
     /* signal that we're done booting.
      * Requires ACCESS_SURFACE_FLINGER permission
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 67afbd6..c56304f 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -558,8 +558,8 @@
     void destroyDisplay(const sp<IBinder>& /*display */) override {}
     sp<IBinder> getBuiltInDisplay(int32_t /*id*/) override { return nullptr; }
     void setTransactionState(const Vector<ComposerState>& /*state*/,
-            const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/)
-            override {}
+                             const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/,
+                             const sp<IBinder>& /*applyToken*/) override {}
     void bootFinished() override {}
     bool authenticateSurfaceTexture(
             const sp<IGraphicBufferProducer>& /*surface*/) const override {
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 4a93be6..02cd9d9 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1568,11 +1568,23 @@
 
 bool SurfaceFlinger::handleMessageTransaction() {
     uint32_t transactionFlags = peekTransactionFlags();
+
+    // Apply any ready transactions in the queues if there are still transactions that have not been
+    // applied, wake up during the next vsync period and check again
+    bool transactionNeeded = false;
+    if (!flushTransactionQueues()) {
+        transactionNeeded = true;
+    }
+
     if (transactionFlags) {
         handleTransaction(transactionFlags);
-        return true;
     }
-    return false;
+
+    if (transactionNeeded) {
+        setTransactionFlags(eTransactionNeeded);
+    }
+
+    return transactionFlags;
 }
 
 void SurfaceFlinger::handleMessageRefresh() {
@@ -3314,6 +3326,26 @@
     return old;
 }
 
+bool SurfaceFlinger::flushTransactionQueues() {
+    Mutex::Autolock _l(mStateLock);
+    auto it = mTransactionQueues.begin();
+    while (it != mTransactionQueues.end()) {
+        auto& [applyToken, transactionQueue] = *it;
+
+        while (!transactionQueue.empty()) {
+            const auto& [states, displays, flags] = transactionQueue.front();
+            if (composerStateContainsUnsignaledFences(states)) {
+                break;
+            }
+            applyTransactionState(states, displays, flags);
+            transactionQueue.pop();
+        }
+
+        it = (transactionQueue.empty()) ? mTransactionQueues.erase(it) : std::next(it, 1);
+    }
+    return mTransactionQueues.empty();
+}
+
 bool SurfaceFlinger::containsAnyInvalidClientState(const Vector<ComposerState>& states) {
     for (const ComposerState& state : states) {
         // Here we need to check that the interface we're given is indeed
@@ -3336,19 +3368,44 @@
     return false;
 }
 
-void SurfaceFlinger::setTransactionState(
-        const Vector<ComposerState>& states,
-        const Vector<DisplayState>& displays,
-        uint32_t flags)
-{
+bool SurfaceFlinger::composerStateContainsUnsignaledFences(const Vector<ComposerState>& states) {
+    for (const ComposerState& state : states) {
+        const layer_state_t& s = state.state;
+        if (!(s.what & layer_state_t::eAcquireFenceChanged)) {
+            continue;
+        }
+        if (s.acquireFence && s.acquireFence->getStatus() == Fence::Status::Unsignaled) {
+            return true;
+        }
+    }
+    return false;
+}
+
+void SurfaceFlinger::setTransactionState(const Vector<ComposerState>& states,
+                                         const Vector<DisplayState>& displays, uint32_t flags,
+                                         const sp<IBinder>& applyToken) {
     ATRACE_CALL();
     Mutex::Autolock _l(mStateLock);
-    uint32_t transactionFlags = 0;
 
     if (containsAnyInvalidClientState(states)) {
         return;
     }
 
+    // If its TransactionQueue already has a pending TransactionState or if it is pending
+    if (mTransactionQueues.find(applyToken) != mTransactionQueues.end() ||
+        composerStateContainsUnsignaledFences(states)) {
+        mTransactionQueues[applyToken].emplace(states, displays, flags);
+        setTransactionFlags(eTransactionNeeded);
+        return;
+    }
+
+    applyTransactionState(states, displays, flags);
+}
+
+void SurfaceFlinger::applyTransactionState(const Vector<ComposerState>& states,
+                                           const Vector<DisplayState>& displays, uint32_t flags) {
+    uint32_t transactionFlags = 0;
+
     if (flags & eAnimation) {
         // For window updates that are part of an animation we must wait for
         // previous animation "frames" to be handled.
@@ -3938,7 +3995,7 @@
     d.width = 0;
     d.height = 0;
     displays.add(d);
-    setTransactionState(state, displays, 0);
+    setTransactionState(state, displays, 0, nullptr);
 
     const auto display = getDisplayDevice(displayToken);
     if (!display) return;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index b1bfb3a..822bb18 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -442,7 +442,8 @@
     virtual void destroyDisplay(const sp<IBinder>& displayToken);
     virtual sp<IBinder> getBuiltInDisplay(int32_t id);
     virtual void setTransactionState(const Vector<ComposerState>& state,
-            const Vector<DisplayState>& displays, uint32_t flags);
+                                     const Vector<DisplayState>& displays, uint32_t flags,
+                                     const sp<IBinder>& applyToken);
     virtual void bootFinished();
     virtual bool authenticateSurfaceTexture(
         const sp<IGraphicBufferProducer>& bufferProducer) const;
@@ -553,6 +554,10 @@
     /* ------------------------------------------------------------------------
      * Transactions
      */
+    void applyTransactionState(const Vector<ComposerState>& state,
+                               const Vector<DisplayState>& displays, uint32_t flags)
+            REQUIRES(mStateLock);
+    bool flushTransactionQueues();
     uint32_t getTransactionFlags(uint32_t flags);
     uint32_t peekTransactionFlags();
     // Can only be called from the main thread or with mStateLock held
@@ -561,6 +566,7 @@
     void latchAndReleaseBuffer(const sp<Layer>& layer);
     void commitTransaction();
     bool containsAnyInvalidClientState(const Vector<ComposerState>& states);
+    bool composerStateContainsUnsignaledFences(const Vector<ComposerState>& states);
     uint32_t setClientStateLocked(const ComposerState& composerState);
     uint32_t setDisplayStateLocked(const DisplayState& s);
     void setDestroyStateLocked(const ComposerState& composerState);
@@ -965,6 +971,22 @@
     uint32_t mTexturePoolSize = 0;
     std::vector<uint32_t> mTexturePool;
 
+    struct IBinderHash {
+        std::size_t operator()(const sp<IBinder>& strongPointer) const {
+            return std::hash<IBinder*>{}(strongPointer.get());
+        }
+    };
+    struct TransactionState {
+        TransactionState(const Vector<ComposerState>& composerStates,
+                         const Vector<DisplayState>& displayStates, uint32_t transactionFlags)
+              : states(composerStates), displays(displayStates), flags(transactionFlags) {}
+
+        Vector<ComposerState> states;
+        Vector<DisplayState> displays;
+        uint32_t flags;
+    };
+    std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IBinderHash> mTransactionQueues;
+
     /* ------------------------------------------------------------------------
      * Feature prototyping
      */
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 604aa7d..f121a95 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -17,6 +17,7 @@
     defaults: ["surfaceflinger_defaults"],
     test_suites: ["device-tests"],
     srcs: [
+        "BufferGenerator.cpp",
         "Credentials_test.cpp",
         "Stress_test.cpp",
         "SurfaceInterceptor_test.cpp",
diff --git a/services/surfaceflinger/tests/BufferGenerator.cpp b/services/surfaceflinger/tests/BufferGenerator.cpp
new file mode 100644
index 0000000..8ddda60
--- /dev/null
+++ b/services/surfaceflinger/tests/BufferGenerator.cpp
@@ -0,0 +1,381 @@
+/*
+ * Copyright 2018 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 <gui/BufferItemConsumer.h>
+#include <gui/Surface.h>
+
+#include <GLES3/gl3.h>
+#include <math/vec2.h>
+#include <math/vec3.h>
+#include <math/vec4.h>
+
+#include "BufferGenerator.h"
+#include "BufferGeneratorShader.h"
+
+namespace android {
+
+/* Used to receive the surfaces and fences from egl. The egl buffers are thrown
+ * away. The fences are sent to the requester via a callback */
+class SurfaceManager {
+public:
+    /* Returns a fence from egl */
+    using BufferCallback = std::function<void(const sp<GraphicBuffer>& buffer, int32_t fence)>;
+
+    /* Listens for a new frame, detaches the buffer and returns the fence
+     * through saved callback. */
+    class BufferListener : public ConsumerBase::FrameAvailableListener {
+    public:
+        BufferListener(sp<IGraphicBufferConsumer> consumer, BufferCallback callback)
+              : mConsumer(consumer), mCallback(callback) {}
+
+        void onFrameAvailable(const BufferItem& /*item*/) {
+            BufferItem item;
+
+            if (mConsumer->acquireBuffer(&item, 0)) return;
+            if (mConsumer->detachBuffer(item.mSlot)) return;
+
+            mCallback(item.mGraphicBuffer, item.mFence->dup());
+        }
+
+    private:
+        sp<IGraphicBufferConsumer> mConsumer;
+        BufferCallback mCallback;
+    };
+
+    /* Creates a buffer listener that waits on a new frame from the buffer
+     * queue. */
+    void initialize(uint32_t width, uint32_t height, android_pixel_format_t format,
+                    BufferCallback callback) {
+        sp<IGraphicBufferProducer> producer;
+        sp<IGraphicBufferConsumer> consumer;
+        BufferQueue::createBufferQueue(&producer, &consumer);
+
+        consumer->setDefaultBufferSize(width, height);
+        consumer->setDefaultBufferFormat(format);
+
+        mBufferItemConsumer = new BufferItemConsumer(consumer, 0);
+
+        mListener = new BufferListener(consumer, callback);
+        mBufferItemConsumer->setFrameAvailableListener(mListener);
+
+        mSurface = new Surface(producer, true);
+    }
+
+    /* Used by Egl manager. The surface is never displayed. */
+    sp<Surface> getSurface() const { return mSurface; }
+
+private:
+    sp<BufferItemConsumer> mBufferItemConsumer;
+    sp<BufferListener> mListener;
+    /* Used by Egl manager. The surface is never displayed */
+    sp<Surface> mSurface;
+};
+
+/* Used to generate valid fences. It is not possible to create a dummy sync
+ * fence for testing. Egl can generate buffers along with a valid fence.
+ * The buffer cannot be guaranteed to be the same format across all devices so
+ * a CPU filled buffer is used instead. The Egl fence is used along with the
+ * CPU filled buffer. */
+class EglManager {
+public:
+    EglManager()
+          : mEglDisplay(EGL_NO_DISPLAY), mEglSurface(EGL_NO_SURFACE), mEglContext(EGL_NO_CONTEXT) {}
+
+    ~EglManager() { cleanup(); }
+
+    int initialize(sp<Surface> surface) {
+        mSurface = surface;
+
+        mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+        if (mEglDisplay == EGL_NO_DISPLAY) return false;
+
+        EGLint major;
+        EGLint minor;
+        if (!eglInitialize(mEglDisplay, &major, &minor)) {
+            ALOGW("Could not initialize EGL");
+            return false;
+        }
+
+        /* We're going to use a 1x1 pbuffer surface later on
+         * The configuration distance doesn't really matter for what we're
+         * trying to do */
+        EGLint configAttrs[] = {EGL_RENDERABLE_TYPE,
+                                EGL_OPENGL_ES2_BIT,
+                                EGL_RED_SIZE,
+                                8,
+                                EGL_GREEN_SIZE,
+                                8,
+                                EGL_BLUE_SIZE,
+                                8,
+                                EGL_ALPHA_SIZE,
+                                0,
+                                EGL_DEPTH_SIZE,
+                                24,
+                                EGL_STENCIL_SIZE,
+                                0,
+                                EGL_NONE};
+
+        EGLConfig configs[1];
+        EGLint configCnt;
+        if (!eglChooseConfig(mEglDisplay, configAttrs, configs, 1, &configCnt)) {
+            ALOGW("Could not select EGL configuration");
+            eglReleaseThread();
+            eglTerminate(mEglDisplay);
+            return false;
+        }
+
+        if (configCnt <= 0) {
+            ALOGW("Could not find EGL configuration");
+            eglReleaseThread();
+            eglTerminate(mEglDisplay);
+            return false;
+        }
+
+        /* These objects are initialized below but the default "null" values are
+         * used to cleanup properly at any point in the initialization sequence */
+        EGLint attrs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
+        mEglContext = eglCreateContext(mEglDisplay, configs[0], EGL_NO_CONTEXT, attrs);
+        if (mEglContext == EGL_NO_CONTEXT) {
+            ALOGW("Could not create EGL context");
+            cleanup();
+            return false;
+        }
+
+        EGLint majorVersion;
+        if (!eglQueryContext(mEglDisplay, mEglContext, EGL_CONTEXT_CLIENT_VERSION, &majorVersion)) {
+            ALOGW("Could not query EGL version");
+            cleanup();
+            return false;
+        }
+
+        if (majorVersion != 3) {
+            ALOGW("Unsupported EGL version");
+            cleanup();
+            return false;
+        }
+
+        EGLint surfaceAttrs[] = {EGL_NONE};
+        mEglSurface = eglCreateWindowSurface(mEglDisplay, configs[0], mSurface.get(), surfaceAttrs);
+        if (mEglSurface == EGL_NO_SURFACE) {
+            ALOGW("Could not create EGL surface");
+            cleanup();
+            return false;
+        }
+
+        if (!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+            ALOGW("Could not change current EGL context");
+            cleanup();
+            return false;
+        }
+
+        return true;
+    }
+
+    void makeCurrent() const { eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext); }
+
+    void present() const { eglSwapBuffers(mEglDisplay, mEglSurface); }
+
+private:
+    void cleanup() {
+        if (mEglDisplay == EGL_NO_DISPLAY) return;
+        if (mEglSurface != EGL_NO_SURFACE) eglDestroySurface(mEglDisplay, mEglSurface);
+        if (mEglContext != EGL_NO_CONTEXT) eglDestroyContext(mEglDisplay, mEglContext);
+
+        eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+        eglReleaseThread();
+        eglTerminate(mEglDisplay);
+    }
+
+    sp<Surface> mSurface;
+    EGLDisplay mEglDisplay;
+    EGLSurface mEglSurface;
+    EGLContext mEglContext;
+};
+
+class Program {
+public:
+    ~Program() {
+        if (mInitialized) {
+            glDetachShader(mProgram, mVertexShader);
+            glDetachShader(mProgram, mFragmentShader);
+
+            glDeleteShader(mVertexShader);
+            glDeleteShader(mFragmentShader);
+
+            glDeleteProgram(mProgram);
+        }
+    }
+
+    bool initialize(const char* vertex, const char* fragment) {
+        mVertexShader = buildShader(vertex, GL_VERTEX_SHADER);
+        if (!mVertexShader) {
+            return false;
+        }
+
+        mFragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER);
+        if (!mFragmentShader) {
+            return false;
+        }
+
+        mProgram = glCreateProgram();
+        glAttachShader(mProgram, mVertexShader);
+        glAttachShader(mProgram, mFragmentShader);
+
+        glLinkProgram(mProgram);
+
+        GLint status;
+        glGetProgramiv(mProgram, GL_LINK_STATUS, &status);
+        if (status != GL_TRUE) {
+            GLint length = 0;
+            glGetProgramiv(mProgram, GL_INFO_LOG_LENGTH, &length);
+            if (length > 1) {
+                GLchar log[length];
+                glGetProgramInfoLog(mProgram, length, nullptr, &log[0]);
+                ALOGE("%s", log);
+            }
+            ALOGE("Error while linking shaders");
+            return false;
+        }
+        mInitialized = true;
+        return true;
+    }
+
+    void use() const { glUseProgram(mProgram); }
+
+    void bindVec4(GLint location, vec4 v) const { glUniform4f(location, v.x, v.y, v.z, v.w); }
+
+    void bindVec3(GLint location, const vec3* v, uint32_t count) const {
+        glUniform3fv(location, count, &(v->x));
+    }
+
+    void bindFloat(GLint location, float v) { glUniform1f(location, v); }
+
+private:
+    GLuint buildShader(const char* source, GLenum type) const {
+        GLuint shader = glCreateShader(type);
+        glShaderSource(shader, 1, &source, nullptr);
+        glCompileShader(shader);
+
+        GLint status;
+        glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
+        if (status != GL_TRUE) {
+            ALOGE("Error while compiling shader of type 0x%x:\n===\n%s\n===", type, source);
+            // Some drivers return wrong values for GL_INFO_LOG_LENGTH
+            // use a fixed size instead
+            GLchar log[512];
+            glGetShaderInfoLog(shader, sizeof(log), nullptr, &log[0]);
+            ALOGE("Shader info log: %s", log);
+            return 0;
+        }
+
+        return shader;
+    }
+
+    GLuint mProgram = 0;
+    GLuint mVertexShader = 0;
+    GLuint mFragmentShader = 0;
+    bool mInitialized = false;
+};
+
+BufferGenerator::BufferGenerator()
+      : mSurfaceManager(new SurfaceManager), mEglManager(new EglManager), mProgram(new Program) {
+    const float width = 1000.0;
+    const float height = 1000.0;
+
+    auto setBufferWithContext =
+            std::bind(setBuffer, std::placeholders::_1, std::placeholders::_2, this);
+    mSurfaceManager->initialize(width, height, HAL_PIXEL_FORMAT_RGBA_8888, setBufferWithContext);
+
+    if (!mEglManager->initialize(mSurfaceManager->getSurface())) return;
+
+    mEglManager->makeCurrent();
+
+    if (!mProgram->initialize(VERTEX_SHADER, FRAGMENT_SHADER)) return;
+    mProgram->use();
+    mProgram->bindVec4(0, vec4{width, height, 1.0f / width, 1.0f / height});
+    mProgram->bindVec3(2, &SPHERICAL_HARMONICS[0], 4);
+
+    glEnableVertexAttribArray(0);
+    glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, &TRIANGLE[0]);
+
+    mInitialized = true;
+}
+
+BufferGenerator::~BufferGenerator() {
+    mEglManager->makeCurrent();
+}
+
+status_t BufferGenerator::get(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) {
+    // mMutex is used to protect get() from getting called by multiple threads at the same time
+    static std::mutex mMutex;
+    std::lock_guard lock(mMutex);
+
+    if (!mInitialized) {
+        if (outBuffer) {
+            *outBuffer = nullptr;
+        }
+        if (*outFence) {
+            *outFence = nullptr;
+        }
+        return -EINVAL;
+    }
+
+    // Generate a buffer and fence. They will be returned through the setBuffer callback
+    mEglManager->makeCurrent();
+
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    const std::chrono::duration<float> time = std::chrono::steady_clock::now() - mEpoch;
+    mProgram->bindFloat(1, time.count());
+
+    glDrawArrays(GL_TRIANGLES, 0, 3);
+
+    mPending = true;
+    mEglManager->present();
+
+    // Wait for the setBuffer callback
+    if (!mConditionVariable.wait_for(mMutex, std::chrono::seconds(2),
+                                     [this] { return !mPending; })) {
+        ALOGE("failed to set buffer and fence");
+        return -ETIME;
+    }
+
+    // Return buffer and fence
+    if (outBuffer) {
+        *outBuffer = mGraphicBuffer;
+    }
+    if (outFence) {
+        *outFence = new Fence(mFence);
+    } else {
+        close(mFence);
+    }
+    mGraphicBuffer = nullptr;
+    mFence = -1;
+
+    return NO_ERROR;
+}
+
+// static
+void BufferGenerator::setBuffer(const sp<GraphicBuffer>& buffer, int32_t fence,
+                                void* bufferGenerator) {
+    BufferGenerator* generator = static_cast<BufferGenerator*>(bufferGenerator);
+    generator->mGraphicBuffer = buffer;
+    generator->mFence = fence;
+    generator->mPending = false;
+    generator->mConditionVariable.notify_all();
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/BufferGenerator.h b/services/surfaceflinger/tests/BufferGenerator.h
new file mode 100644
index 0000000..a3ffe86
--- /dev/null
+++ b/services/surfaceflinger/tests/BufferGenerator.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#pragma once
+
+#include <condition_variable>
+#include <mutex>
+
+#include <ui/GraphicBuffer.h>
+
+namespace android {
+
+class SurfaceManager;
+class EglManager;
+class Program;
+
+class BufferGenerator {
+public:
+    BufferGenerator();
+    ~BufferGenerator();
+
+    /* Get a new fence */
+    status_t get(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence);
+
+    /* Static callback that sets the fence on a particular instance */
+    static void setBuffer(const sp<GraphicBuffer>& buffer, int32_t fence, void* fenceGenerator);
+
+private:
+    bool mInitialized = false;
+
+    std::unique_ptr<SurfaceManager> mSurfaceManager;
+    std::unique_ptr<EglManager> mEglManager;
+    std::unique_ptr<Program> mProgram;
+
+    std::condition_variable_any mConditionVariable;
+
+    sp<GraphicBuffer> mGraphicBuffer;
+    int32_t mFence = -1;
+    bool mPending = false;
+
+    using Epoch = std::chrono::time_point<std::chrono::steady_clock>;
+    Epoch mEpoch = std::chrono::steady_clock::now();
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/BufferGeneratorShader.h b/services/surfaceflinger/tests/BufferGeneratorShader.h
new file mode 100644
index 0000000..564cda3
--- /dev/null
+++ b/services/surfaceflinger/tests/BufferGeneratorShader.h
@@ -0,0 +1,355 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#pragma once
+
+#include <GLES3/gl3.h>
+#include <math/vec2.h>
+#include <math/vec3.h>
+#include <math/vec4.h>
+
+static const char* VERTEX_SHADER = R"SHADER__(#version 300 es
+precision highp float;
+
+layout(location = 0) in vec4 mesh_position;
+
+void main() {
+    gl_Position = mesh_position;
+}
+)SHADER__";
+
+static const char* FRAGMENT_SHADER = R"SHADER__(#version 300 es
+precision highp float;
+
+layout(location = 0) uniform vec4 resolution;
+layout(location = 1) uniform float time;
+layout(location = 2) uniform vec3[4] SPHERICAL_HARMONICS;
+
+layout(location = 0) out vec4 fragColor;
+
+#define saturate(x) clamp(x, 0.0, 1.0)
+#define PI 3.14159265359
+
+//------------------------------------------------------------------------------
+// Distance field functions
+//------------------------------------------------------------------------------
+
+float sdPlane(in vec3 p) {
+    return p.y;
+}
+
+float sdSphere(in vec3 p, float s) {
+    return length(p) - s;
+}
+
+float sdTorus(in vec3 p, in vec2 t) {
+    return length(vec2(length(p.xz) - t.x, p.y)) - t.y;
+}
+
+vec2 opUnion(vec2 d1, vec2 d2) {
+    return d1.x < d2.x ? d1 : d2;
+}
+
+vec2 scene(in vec3 position) {
+    vec2 scene = opUnion(
+          vec2(sdPlane(position), 1.0),
+          vec2(sdSphere(position - vec3(0.0, 0.4, 0.0), 0.4), 12.0)
+    );
+    return scene;
+}
+
+//------------------------------------------------------------------------------
+// Ray casting
+//------------------------------------------------------------------------------
+
+float shadow(in vec3 origin, in vec3 direction, in float tmin, in float tmax) {
+    float hit = 1.0;
+
+    for (float t = tmin; t < tmax; ) {
+        float h = scene(origin + direction * t).x;
+        if (h < 0.001) return 0.0;
+        t += h;
+        hit = min(hit, 10.0 * h / t);
+    }
+
+    return clamp(hit, 0.0, 1.0);
+}
+
+vec2 traceRay(in vec3 origin, in vec3 direction) {
+    float tmin = 0.02;
+    float tmax = 20.0;
+
+    float material = -1.0;
+    float t = tmin;
+
+    for ( ; t < tmax; ) {
+        vec2 hit = scene(origin + direction * t);
+        if (hit.x < 0.002 || t > tmax) break;
+        t += hit.x;
+        material = hit.y;
+    }
+
+    if (t > tmax) {
+        material = -1.0;
+    }
+
+    return vec2(t, material);
+}
+
+vec3 normal(in vec3 position) {
+    vec3 epsilon = vec3(0.001, 0.0, 0.0);
+    vec3 n = vec3(
+          scene(position + epsilon.xyy).x - scene(position - epsilon.xyy).x,
+          scene(position + epsilon.yxy).x - scene(position - epsilon.yxy).x,
+          scene(position + epsilon.yyx).x - scene(position - epsilon.yyx).x);
+    return normalize(n);
+}
+
+//------------------------------------------------------------------------------
+// BRDF
+//------------------------------------------------------------------------------
+
+float pow5(float x) {
+    float x2 = x * x;
+    return x2 * x2 * x;
+}
+
+float D_GGX(float linearRoughness, float NoH, const vec3 h) {
+    // Walter et al. 2007, "Microfacet Models for Refraction through Rough Surfaces"
+    float oneMinusNoHSquared = 1.0 - NoH * NoH;
+    float a = NoH * linearRoughness;
+    float k = linearRoughness / (oneMinusNoHSquared + a * a);
+    float d = k * k * (1.0 / PI);
+    return d;
+}
+
+float V_SmithGGXCorrelated(float linearRoughness, float NoV, float NoL) {
+    // Heitz 2014, "Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs"
+    float a2 = linearRoughness * linearRoughness;
+    float GGXV = NoL * sqrt((NoV - a2 * NoV) * NoV + a2);
+    float GGXL = NoV * sqrt((NoL - a2 * NoL) * NoL + a2);
+    return 0.5 / (GGXV + GGXL);
+}
+
+vec3 F_Schlick(const vec3 f0, float VoH) {
+    // Schlick 1994, "An Inexpensive BRDF Model for Physically-Based Rendering"
+    return f0 + (vec3(1.0) - f0) * pow5(1.0 - VoH);
+}
+
+float F_Schlick(float f0, float f90, float VoH) {
+    return f0 + (f90 - f0) * pow5(1.0 - VoH);
+}
+
+float Fd_Burley(float linearRoughness, float NoV, float NoL, float LoH) {
+    // Burley 2012, "Physically-Based Shading at Disney"
+    float f90 = 0.5 + 2.0 * linearRoughness * LoH * LoH;
+    float lightScatter = F_Schlick(1.0, f90, NoL);
+    float viewScatter  = F_Schlick(1.0, f90, NoV);
+    return lightScatter * viewScatter * (1.0 / PI);
+}
+
+float Fd_Lambert() {
+    return 1.0 / PI;
+}
+
+//------------------------------------------------------------------------------
+// Indirect lighting
+//------------------------------------------------------------------------------
+
+vec3 Irradiance_SphericalHarmonics(const vec3 n) {
+    return max(
+          SPHERICAL_HARMONICS[0]
+        + SPHERICAL_HARMONICS[1] * (n.y)
+        + SPHERICAL_HARMONICS[2] * (n.z)
+        + SPHERICAL_HARMONICS[3] * (n.x)
+        , 0.0);
+}
+
+vec2 PrefilteredDFG_Karis(float roughness, float NoV) {
+    // Karis 2014, "Physically Based Material on Mobile"
+    const vec4 c0 = vec4(-1.0, -0.0275, -0.572,  0.022);
+    const vec4 c1 = vec4( 1.0,  0.0425,  1.040, -0.040);
+
+    vec4 r = roughness * c0 + c1;
+    float a004 = min(r.x * r.x, exp2(-9.28 * NoV)) * r.x + r.y;
+
+    return vec2(-1.04, 1.04) * a004 + r.zw;
+}
+
+//------------------------------------------------------------------------------
+// Tone mapping and transfer functions
+//------------------------------------------------------------------------------
+
+vec3 Tonemap_ACES(const vec3 x) {
+    // Narkowicz 2015, "ACES Filmic Tone Mapping Curve"
+    const float a = 2.51;
+    const float b = 0.03;
+    const float c = 2.43;
+    const float d = 0.59;
+    const float e = 0.14;
+    return (x * (a * x + b)) / (x * (c * x + d) + e);
+}
+
+vec3 OECF_sRGBFast(const vec3 linear) {
+    return pow(linear, vec3(1.0 / 2.2));
+}
+
+//------------------------------------------------------------------------------
+// Rendering
+//------------------------------------------------------------------------------
+
+vec3 render(in vec3 origin, in vec3 direction, out float distance) {
+    // Sky gradient
+    vec3 color = vec3(0.65, 0.85, 1.0) + direction.y * 0.72;
+
+    // (distance, material)
+    vec2 hit = traceRay(origin, direction);
+    distance = hit.x;
+    float material = hit.y;
+
+    // We've hit something in the scene
+    if (material > 0.0) {
+        vec3 position = origin + distance * direction;
+
+        vec3 v = normalize(-direction);
+        vec3 n = normal(position);
+        vec3 l = normalize(vec3(0.6, 0.7, -0.7));
+        vec3 h = normalize(v + l);
+        vec3 r = normalize(reflect(direction, n));
+
+        float NoV = abs(dot(n, v)) + 1e-5;
+        float NoL = saturate(dot(n, l));
+        float NoH = saturate(dot(n, h));
+        float LoH = saturate(dot(l, h));
+
+        vec3 baseColor = vec3(0.0);
+        float roughness = 0.0;
+        float metallic = 0.0;
+
+        float intensity = 2.0;
+        float indirectIntensity = 0.64;
+
+        if (material < 4.0)  {
+            // Checkerboard floor
+            float f = mod(floor(6.0 * position.z) + floor(6.0 * position.x), 2.0);
+            baseColor = 0.4 + f * vec3(0.6);
+            roughness = 0.1;
+        } else if (material < 16.0) {
+            // Metallic objects
+            baseColor = vec3(0.3, 0.0, 0.0);
+            roughness = 0.2;
+        }
+
+        float linearRoughness = roughness * roughness;
+        vec3 diffuseColor = (1.0 - metallic) * baseColor.rgb;
+        vec3 f0 = 0.04 * (1.0 - metallic) + baseColor.rgb * metallic;
+
+        float attenuation = shadow(position, l, 0.02, 2.5);
+
+        // specular BRDF
+        float D = D_GGX(linearRoughness, NoH, h);
+        float V = V_SmithGGXCorrelated(linearRoughness, NoV, NoL);
+        vec3  F = F_Schlick(f0, LoH);
+        vec3 Fr = (D * V) * F;
+
+        // diffuse BRDF
+        vec3 Fd = diffuseColor * Fd_Burley(linearRoughness, NoV, NoL, LoH);
+
+        color = Fd + Fr;
+        color *= (intensity * attenuation * NoL) * vec3(0.98, 0.92, 0.89);
+
+        // diffuse indirect
+        vec3 indirectDiffuse = Irradiance_SphericalHarmonics(n) * Fd_Lambert();
+
+        vec2 indirectHit = traceRay(position, r);
+        vec3 indirectSpecular = vec3(0.65, 0.85, 1.0) + r.y * 0.72;
+        if (indirectHit.y > 0.0) {
+            if (indirectHit.y < 4.0)  {
+                vec3 indirectPosition = position + indirectHit.x * r;
+                // Checkerboard floor
+                float f = mod(floor(6.0 * indirectPosition.z) + floor(6.0 * indirectPosition.x), 2.0);
+                indirectSpecular = 0.4 + f * vec3(0.6);
+            } else if (indirectHit.y < 16.0) {
+                // Metallic objects
+                indirectSpecular = vec3(0.3, 0.0, 0.0);
+            }
+        }
+
+        // indirect contribution
+        vec2 dfg = PrefilteredDFG_Karis(roughness, NoV);
+        vec3 specularColor = f0 * dfg.x + dfg.y;
+        vec3 ibl = diffuseColor * indirectDiffuse + indirectSpecular * specularColor;
+
+        color += ibl * indirectIntensity;
+    }
+
+    return color;
+}
+
+//------------------------------------------------------------------------------
+// Setup and execution
+//------------------------------------------------------------------------------
+
+mat3 setCamera(in vec3 origin, in vec3 target, float rotation) {
+    vec3 forward = normalize(target - origin);
+    vec3 orientation = vec3(sin(rotation), cos(rotation), 0.0);
+    vec3 left = normalize(cross(forward, orientation));
+    vec3 up = normalize(cross(left, forward));
+    return mat3(left, up, forward);
+}
+
+void main() {
+    // Normalized coordinates
+    vec2 p = -1.0 + 2.0 * gl_FragCoord.xy / resolution.xy;
+    // Aspect ratio
+    p.x *= resolution.x / resolution.y;
+
+    // Camera position and "look at"
+    vec3 origin = vec3(0.0, 1.0, 0.0);
+    vec3 target = vec3(0.0);
+
+    origin.x += 2.0 * cos(time * 0.2);
+    origin.z += 2.0 * sin(time * 0.2);
+
+    mat3 toWorld = setCamera(origin, target, 0.0);
+    vec3 direction = toWorld * normalize(vec3(p.xy, 2.0));
+
+    // Render scene
+    float distance;
+    vec3 color = render(origin, direction, distance);
+
+    // Tone mapping
+    color = Tonemap_ACES(color);
+
+    // Exponential distance fog
+    color = mix(color, 0.8 * vec3(0.7, 0.8, 1.0), 1.0 - exp2(-0.011 * distance * distance));
+
+    // Gamma compression
+    color = OECF_sRGBFast(color);
+
+    fragColor = vec4(color, 1.0);
+}
+)SHADER__";
+
+static const android::vec3 SPHERICAL_HARMONICS[4] =
+        {{0.754554516862612, 0.748542953903366, 0.790921515418539},
+         {-0.083856548007422, 0.092533500963210, 0.322764661032516},
+         {0.308152705331738, 0.366796330467391, 0.466698181299906},
+         {-0.188884931542396, -0.277402551592231, -0.377844212327557}};
+
+static const android::vec4 TRIANGLE[3] = {{-1.0f, -1.0f, 1.0f, 1.0f},
+                                          {3.0f, -1.0f, 1.0f, 1.0f},
+                                          {-1.0f, 3.0f, 1.0f, 1.0f}};
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index 037d32f..d118ad6 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -28,7 +28,6 @@
 
 #include <gui/ISurfaceComposer.h>
 #include <gui/LayerState.h>
-
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
 #include <private/gui/ComposerService.h>
@@ -41,6 +40,8 @@
 #include <math.h>
 #include <math/vec3.h>
 
+#include "BufferGenerator.h"
+
 namespace android {
 
 namespace {
@@ -478,6 +479,11 @@
         return screenshot;
     }
 
+    static status_t getBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) {
+        static BufferGenerator bufferGenerator;
+        return bufferGenerator.get(outBuffer, outFence);
+    }
+
     sp<SurfaceComposerClient> mClient;
 
     sp<IBinder> mDisplay;
@@ -2164,6 +2170,36 @@
 
 TEST_F(LayerTransactionTest, SetFenceBasic_BufferState) {
     sp<SurfaceControl> layer;
+    Transaction transaction;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    sp<GraphicBuffer> buffer =
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY,
+                              "test");
+    fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
+
+    sp<Fence> fence;
+    if (getBuffer(nullptr, &fence) != NO_ERROR) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    Transaction().setBuffer(layer, buffer).setAcquireFence(layer, fence).apply();
+
+    status_t status = fence->wait(1000);
+    ASSERT_NE(static_cast<status_t>(Fence::Status::Unsignaled), status);
+    std::this_thread::sleep_for(200ms);
+
+    auto shot = screenshot();
+    shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+}
+
+TEST_F(LayerTransactionTest, SetFenceNull_BufferState) {
+    sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
 
@@ -2514,7 +2550,7 @@
     }
 
     void addSurface(ExpectedResult::Transaction transactionResult, const sp<SurfaceControl>& layer,
-                    ExpectedResult::Buffer bufferResult = NOT_ACQUIRED,
+                    ExpectedResult::Buffer bufferResult = ACQUIRED,
                     ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) {
         mTransactionResult = transactionResult;
         mExpectedSurfaceResults.emplace(std::piecewise_construct,
@@ -2524,7 +2560,7 @@
 
     void addSurfaces(ExpectedResult::Transaction transactionResult,
                      const std::vector<sp<SurfaceControl>>& layers,
-                     ExpectedResult::Buffer bufferResult = NOT_ACQUIRED,
+                     ExpectedResult::Buffer bufferResult = ACQUIRED,
                      ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) {
         for (const auto& layer : layers) {
             addSurface(transactionResult, layer, bufferResult, previousBufferResult);
@@ -2631,24 +2667,22 @@
         return createLayer(mClient, "test", 0, 0, ISurfaceComposerClient::eFXSurfaceBufferState);
     }
 
-    static void fillTransaction(Transaction& transaction, CallbackHelper* callbackHelper,
-                                const sp<SurfaceControl>& layer = nullptr) {
+    static int fillTransaction(Transaction& transaction, CallbackHelper* callbackHelper,
+                               const sp<SurfaceControl>& layer = nullptr) {
         if (layer) {
-            sp<GraphicBuffer> buffer =
-                    new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
-                                      BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                              BufferUsage::COMPOSER_OVERLAY |
-                                              BufferUsage::GPU_TEXTURE,
-                                      "test");
-            fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
-
-            sp<Fence> fence = new Fence(-1);
+            sp<GraphicBuffer> buffer;
+            sp<Fence> fence;
+            int err = getBuffer(&buffer, &fence);
+            if (err != NO_ERROR) {
+                return err;
+            }
 
             transaction.setBuffer(layer, buffer).setAcquireFence(layer, fence);
         }
 
         transaction.addTransactionCompletedCallback(callbackHelper->function,
                                                     callbackHelper->getContext());
+        return NO_ERROR;
     }
 
     static void waitForCallback(CallbackHelper& helper, const ExpectedResult& expectedResult,
@@ -2680,7 +2714,11 @@
 
     Transaction transaction;
     CallbackHelper callback;
-    fillTransaction(transaction, &callback, layer);
+    int err = fillTransaction(transaction, &callback, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
 
     transaction.apply();
 
@@ -2695,19 +2733,28 @@
 
     Transaction transaction;
     CallbackHelper callback;
-    fillTransaction(transaction, &callback);
+    int err = fillTransaction(transaction, &callback);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
 
     transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
 
     ExpectedResult expected;
-    expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer);
+    expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer,
+                        ExpectedResult::Buffer::NOT_ACQUIRED);
     EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
 }
 
 TEST_F(LayerCallbackTest, NoStateChange) {
     Transaction transaction;
     CallbackHelper callback;
-    fillTransaction(transaction, &callback);
+    int err = fillTransaction(transaction, &callback);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
 
     transaction.apply();
 
@@ -2721,7 +2768,11 @@
 
     Transaction transaction;
     CallbackHelper callback;
-    fillTransaction(transaction, &callback, layer);
+    int err = fillTransaction(transaction, &callback, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
 
     transaction.setFrame(layer, Rect(-100, -100, 100, 100)).apply();
 
@@ -2737,8 +2788,16 @@
 
     Transaction transaction1, transaction2;
     CallbackHelper callback1, callback2;
-    fillTransaction(transaction1, &callback1, layer1);
-    fillTransaction(transaction2, &callback2, layer2);
+    int err = fillTransaction(transaction1, &callback1, layer1);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+    err = fillTransaction(transaction2, &callback2, layer2);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
 
     transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
     transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
@@ -2756,8 +2815,16 @@
 
     Transaction transaction1, transaction2;
     CallbackHelper callback;
-    fillTransaction(transaction1, &callback, layer1);
-    fillTransaction(transaction2, &callback, layer2);
+    int err = fillTransaction(transaction1, &callback, layer1);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+    err = fillTransaction(transaction2, &callback, layer2);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
 
     transaction2.merge(std::move(transaction1)).apply();
 
@@ -2773,8 +2840,16 @@
 
     Transaction transaction1, transaction2;
     CallbackHelper callback1, callback2;
-    fillTransaction(transaction1, &callback1, layer);
-    fillTransaction(transaction2, &callback2, layer);
+    int err = fillTransaction(transaction1, &callback1, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+    err = fillTransaction(transaction2, &callback2, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
 
     transaction2.merge(std::move(transaction1)).apply();
 
@@ -2784,25 +2859,6 @@
     EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
 }
 
-TEST_F(LayerCallbackTest, Merge_SingleBuffer) {
-    sp<SurfaceControl> layer1, layer2;
-    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
-    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
-
-    Transaction transaction1, transaction2;
-    CallbackHelper callback1, callback2;
-    fillTransaction(transaction1, &callback1, layer1);
-    fillTransaction(transaction2, &callback2);
-
-    transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
-    transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
-
-    ExpectedResult expected;
-    expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
-}
-
 TEST_F(LayerCallbackTest, Merge_DifferentClients) {
     sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
             client2(new SurfaceComposerClient);
@@ -2818,8 +2874,16 @@
 
     Transaction transaction1, transaction2;
     CallbackHelper callback1, callback2;
-    fillTransaction(transaction1, &callback1, layer1);
-    fillTransaction(transaction2, &callback2, layer2);
+    int err = fillTransaction(transaction1, &callback1, layer1);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+    err = fillTransaction(transaction2, &callback2, layer2);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
 
     transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
     transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
@@ -2837,13 +2901,17 @@
     Transaction transaction;
     CallbackHelper callback;
     for (size_t i = 0; i < 10; i++) {
-        fillTransaction(transaction, &callback, layer);
+        int err = fillTransaction(transaction, &callback, layer);
+        if (err) {
+            GTEST_SUCCEED() << "test not supported";
+            return;
+        }
 
         transaction.apply();
 
         ExpectedResult expected;
         expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
-                            ExpectedResult::Buffer::NOT_ACQUIRED,
+                            ExpectedResult::Buffer::ACQUIRED,
                             (i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED
                                      : ExpectedResult::PreviousBuffer::RELEASED);
         EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected));
@@ -2861,10 +2929,18 @@
         ExpectedResult expected;
 
         if (i == 0) {
-            fillTransaction(transaction, &callback, layer);
+            int err = fillTransaction(transaction, &callback, layer);
+            if (err) {
+                GTEST_SUCCEED() << "test not supported";
+                return;
+            }
             expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
         } else {
-            fillTransaction(transaction, &callback);
+            int err = fillTransaction(transaction, &callback);
+            if (err) {
+                GTEST_SUCCEED() << "test not supported";
+                return;
+            }
         }
 
         transaction.apply();
@@ -2882,9 +2958,17 @@
     CallbackHelper callback;
     for (size_t i = 0; i < 10; i++) {
         if (i == 0) {
-            fillTransaction(transaction, &callback, layer);
+            int err = fillTransaction(transaction, &callback, layer);
+            if (err) {
+                GTEST_SUCCEED() << "test not supported";
+                return;
+            }
         } else {
-            fillTransaction(transaction, &callback);
+            int err = fillTransaction(transaction, &callback);
+            if (err) {
+                GTEST_SUCCEED() << "test not supported";
+                return;
+            }
         }
 
         transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
@@ -2892,7 +2976,9 @@
         ExpectedResult expected;
         expected.addSurface((i == 0) ? ExpectedResult::Transaction::PRESENTED
                                      : ExpectedResult::Transaction::NOT_PRESENTED,
-                            layer);
+                            layer,
+                            (i == 0) ? ExpectedResult::Buffer::ACQUIRED
+                                     : ExpectedResult::Buffer::NOT_ACQUIRED);
         EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, i == 0));
     }
     ASSERT_NO_FATAL_FAILURE(callback.verifyFinalState());
@@ -2906,15 +2992,23 @@
     Transaction transaction1, transaction2;
     CallbackHelper callback1, callback2;
     for (size_t i = 0; i < 10; i++) {
-        fillTransaction(transaction1, &callback1, layer1);
-        fillTransaction(transaction2, &callback2, layer2);
+        int err = fillTransaction(transaction1, &callback1, layer1);
+        if (err) {
+            GTEST_SUCCEED() << "test not supported";
+            return;
+        }
+        err = fillTransaction(transaction2, &callback2, layer2);
+        if (err) {
+            GTEST_SUCCEED() << "test not supported";
+            return;
+        }
 
         transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
         transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
 
         ExpectedResult expected;
         expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2},
-                             ExpectedResult::Buffer::NOT_ACQUIRED,
+                             ExpectedResult::Buffer::ACQUIRED,
                              (i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED
                                       : ExpectedResult::PreviousBuffer::RELEASED);
         EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected));
@@ -2939,15 +3033,23 @@
     Transaction transaction1, transaction2;
     CallbackHelper callback1, callback2;
     for (size_t i = 0; i < 10; i++) {
-        fillTransaction(transaction1, &callback1, layer1);
-        fillTransaction(transaction2, &callback2, layer2);
+        int err = fillTransaction(transaction1, &callback1, layer1);
+        if (err) {
+            GTEST_SUCCEED() << "test not supported";
+            return;
+        }
+        err = fillTransaction(transaction2, &callback2, layer2);
+        if (err) {
+            GTEST_SUCCEED() << "test not supported";
+            return;
+        }
 
         transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
         transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
 
         ExpectedResult expected;
         expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2},
-                             ExpectedResult::Buffer::NOT_ACQUIRED,
+                             ExpectedResult::Buffer::ACQUIRED,
                              (i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED
                                       : ExpectedResult::PreviousBuffer::RELEASED);
         EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected));
@@ -2973,8 +3075,16 @@
     CallbackHelper callback1, callback2;
 
     // Normal call to set up test
-    fillTransaction(transaction1, &callback1, layer1);
-    fillTransaction(transaction2, &callback2, layer2);
+    int err = fillTransaction(transaction1, &callback1, layer1);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+    err = fillTransaction(transaction2, &callback2, layer2);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
 
     transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
     transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
@@ -2986,8 +3096,16 @@
     expected.reset();
 
     // Test
-    fillTransaction(transaction1, &callback1);
-    fillTransaction(transaction2, &callback2);
+    err = fillTransaction(transaction1, &callback1);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+    err = fillTransaction(transaction2, &callback2);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
 
     transaction2.merge(std::move(transaction1)).apply();
 
@@ -3012,8 +3130,16 @@
     CallbackHelper callback1, callback2;
 
     // Normal call to set up test
-    fillTransaction(transaction1, &callback1, layer1);
-    fillTransaction(transaction2, &callback2, layer2);
+    int err = fillTransaction(transaction1, &callback1, layer1);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+    err = fillTransaction(transaction2, &callback2, layer2);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
 
     transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
     transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
@@ -3025,12 +3151,21 @@
     expected.reset();
 
     // Test
-    fillTransaction(transaction1, &callback1);
-    fillTransaction(transaction2, &callback2);
+    err = fillTransaction(transaction1, &callback1);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+    err = fillTransaction(transaction2, &callback2);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
 
     transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
 
-    expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer2);
+    expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer2,
+                        ExpectedResult::Buffer::NOT_ACQUIRED);
     EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
     EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
 }
@@ -3047,13 +3182,16 @@
     for (auto& expected : expectedResults) {
         expected.reset();
         expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
-                            ExpectedResult::Buffer::NOT_ACQUIRED, previousBufferResult);
+                            ExpectedResult::Buffer::ACQUIRED, previousBufferResult);
         previousBufferResult = ExpectedResult::PreviousBuffer::RELEASED;
 
-        fillTransaction(transaction, &callback, layer);
+        int err = fillTransaction(transaction, &callback, layer);
+        if (err) {
+            GTEST_SUCCEED() << "test not supported";
+            return;
+        }
 
         transaction.apply();
-        std::this_thread::sleep_for(200ms);
     }
     EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
 }
@@ -3062,23 +3200,33 @@
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
 
+    // Normal call to set up test
     Transaction transaction;
     CallbackHelper callback;
+    int err = fillTransaction(transaction, &callback, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction.apply();
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+
+    // Test
     std::vector<ExpectedResult> expectedResults(50);
-    bool first = true;
     for (auto& expected : expectedResults) {
         expected.reset();
 
-        if (first) {
-            fillTransaction(transaction, &callback, layer);
-            expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
-            first = false;
-        } else {
-            fillTransaction(transaction, &callback);
+        err = fillTransaction(transaction, &callback);
+        if (err) {
+            GTEST_SUCCEED() << "test not supported";
+            return;
         }
 
         transaction.apply();
-        std::this_thread::sleep_for(200ms);
     }
     EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
 }
@@ -3090,7 +3238,11 @@
     // Normal call to set up test
     Transaction transaction;
     CallbackHelper callback;
-    fillTransaction(transaction, &callback, layer);
+    int err = fillTransaction(transaction, &callback, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
 
     transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
 
@@ -3102,13 +3254,16 @@
     std::vector<ExpectedResult> expectedResults(50);
     for (auto& expected : expectedResults) {
         expected.reset();
-        expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer);
+        expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer,
+                            ExpectedResult::Buffer::NOT_ACQUIRED);
 
-        fillTransaction(transaction, &callback);
+        err = fillTransaction(transaction, &callback);
+        if (err) {
+            GTEST_SUCCEED() << "test not supported";
+            return;
+        }
 
         transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
-
-        std::this_thread::sleep_for(200ms);
     }
     EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
 }