First patch for async renderengine.

1. return a std::future object from DrawLayers() instead to make it
asynchronously
2. retire drawFence value from drawLayers() parameter list.

Bug: 180657548
Test: android.hardware.graphics.composer@2.2-vts, libcompositionengine_test, libsurfaceflinger_unittest and
librenderengine_test pass

Change-Id: If63c11762d1cce7b053cc72aaeaed6dd7904877f
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index 0c5a851..2174df5 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -95,5 +95,16 @@
                         "output buffer not gpu writeable");
 }
 
+std::future<RenderEngineResult> RenderEngine::drawLayers(
+        const DisplaySettings& display, const std::vector<const LayerSettings*>& layers,
+        const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache,
+        base::unique_fd&& bufferFence) {
+    const auto resultPromise = std::make_shared<std::promise<RenderEngineResult>>();
+    std::future<RenderEngineResult> resultFuture = resultPromise->get_future();
+    drawLayersInternal(std::move(resultPromise), display, layers, buffer, useFramebufferCache,
+                       std::move(bufferFence));
+    return resultFuture;
+}
+
 } // namespace renderengine
 } // namespace android
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index b1e1014..2375cb7 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -1078,15 +1078,16 @@
     return image;
 }
 
-status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
-                                      const std::vector<const LayerSettings*>& layers,
-                                      const std::shared_ptr<ExternalTexture>& buffer,
-                                      const bool useFramebufferCache, base::unique_fd&& bufferFence,
-                                      base::unique_fd* drawFence) {
+void GLESRenderEngine::drawLayersInternal(
+        const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+        const DisplaySettings& display, const std::vector<const LayerSettings*>& layers,
+        const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache,
+        base::unique_fd&& bufferFence) {
     ATRACE_CALL();
     if (layers.empty()) {
         ALOGV("Drawing empty layer stack");
-        return NO_ERROR;
+        resultPromise->set_value({NO_ERROR, base::unique_fd()});
+        return;
     }
 
     if (bufferFence.get() >= 0) {
@@ -1100,7 +1101,8 @@
 
     if (buffer == nullptr) {
         ALOGE("No output buffer provided. Aborting GPU composition.");
-        return BAD_VALUE;
+        resultPromise->set_value({BAD_VALUE, base::unique_fd()});
+        return;
     }
 
     validateOutputBufferUsage(buffer->getBuffer());
@@ -1128,7 +1130,8 @@
             ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
                   buffer->getBuffer()->handle);
             checkErrors();
-            return fbo->getStatus();
+            resultPromise->set_value({fbo->getStatus(), base::unique_fd()});
+            return;
         }
         setViewportAndProjection(display.physicalDisplay, display.clip);
     } else {
@@ -1139,7 +1142,8 @@
             ALOGE("Failed to prepare blur filter! Aborting GPU composition for buffer (%p).",
                   buffer->getBuffer()->handle);
             checkErrors();
-            return status;
+            resultPromise->set_value({status, base::unique_fd()});
+            return;
         }
     }
 
@@ -1172,7 +1176,8 @@
                 ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
                       buffer->getBuffer()->handle);
                 checkErrors("Can't render first blur pass");
-                return status;
+                resultPromise->set_value({status, base::unique_fd()});
+                return;
             }
 
             if (blurLayers.size() == 0) {
@@ -1194,7 +1199,8 @@
                 ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
                       buffer->getBuffer()->handle);
                 checkErrors("Can't bind native framebuffer");
-                return status;
+                resultPromise->set_value({status, base::unique_fd()});
+                return;
             }
 
             status = mBlurFilter->render(blurLayersSize > 1);
@@ -1202,7 +1208,8 @@
                 ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
                       buffer->getBuffer()->handle);
                 checkErrors("Can't render blur filter");
-                return status;
+                resultPromise->set_value({status, base::unique_fd()});
+                return;
             }
         }
 
@@ -1289,30 +1296,31 @@
         }
     }
 
-    if (drawFence != nullptr) {
-        *drawFence = flush();
-    }
+    base::unique_fd drawFence = flush();
+
     // If flush failed or we don't support native fences, we need to force the
     // gl command stream to be executed.
-    if (drawFence == nullptr || drawFence->get() < 0) {
+    if (drawFence.get() < 0) {
         bool success = finish();
         if (!success) {
             ALOGE("Failed to flush RenderEngine commands");
             checkErrors();
             // Chances are, something illegal happened (either the caller passed
             // us bad parameters, or we messed up our shader generation).
-            return INVALID_OPERATION;
+            resultPromise->set_value({INVALID_OPERATION, std::move(drawFence)});
+            return;
         }
         mLastDrawFence = nullptr;
     } else {
         // The caller takes ownership of drawFence, so we need to duplicate the
         // fd here.
-        mLastDrawFence = new Fence(dup(drawFence->get()));
+        mLastDrawFence = new Fence(dup(drawFence.get()));
     }
     mPriorResourcesCleaned = false;
 
     checkErrors();
-    return NO_ERROR;
+    resultPromise->set_value({NO_ERROR, std::move(drawFence)});
+    return;
 }
 
 void GLESRenderEngine::setViewportAndProjection(Rect viewport, Rect clip) {
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 14627ce..c4adfdf 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -63,11 +63,6 @@
     bool isProtected() const override { return mInProtectedContext; }
     bool supportsProtectedContent() const override;
     void useProtectedContext(bool useProtectedContext) override;
-    status_t drawLayers(const DisplaySettings& display,
-                        const std::vector<const LayerSettings*>& layers,
-                        const std::shared_ptr<ExternalTexture>& buffer,
-                        const bool useFramebufferCache, base::unique_fd&& bufferFence,
-                        base::unique_fd* drawFence) override;
     void cleanupPostRender() override;
     int getContextPriority() override;
     bool supportsBackgroundBlur() override { return mBlurFilter != nullptr; }
@@ -107,6 +102,11 @@
             EXCLUDES(mRenderingMutex);
     void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex);
     bool canSkipPostRenderCleanup() const override;
+    void drawLayersInternal(const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+                            const DisplaySettings& display,
+                            const std::vector<const LayerSettings*>& layers,
+                            const std::shared_ptr<ExternalTexture>& buffer,
+                            const bool useFramebufferCache, base::unique_fd&& bufferFence) override;
 
 private:
     friend class BindNativeBufferAsFramebuffer;
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 967cf5d..701c1f2 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -68,6 +68,7 @@
 class Mesh;
 class Texture;
 struct RenderEngineCreationArgs;
+struct RenderEngineResult;
 
 namespace threaded {
 class RenderEngineThreaded;
@@ -156,17 +157,12 @@
     // parameter does nothing.
     // @param bufferFence Fence signalling that the buffer is ready to be drawn
     // to.
-    // @param drawFence A pointer to a fence, which will fire when the buffer
-    // has been drawn to and is ready to be examined. The fence will be
-    // initialized by this method. The caller will be responsible for owning the
-    // fence.
-    // @return An error code indicating whether drawing was successful. For
-    // now, this always returns NO_ERROR.
-    virtual status_t drawLayers(const DisplaySettings& display,
-                                const std::vector<const LayerSettings*>& layers,
-                                const std::shared_ptr<ExternalTexture>& buffer,
-                                const bool useFramebufferCache, base::unique_fd&& bufferFence,
-                                base::unique_fd* drawFence) = 0;
+    // @return A future object of RenderEngineResult struct indicating whether
+    // drawing was successful in async mode.
+    virtual std::future<RenderEngineResult> drawLayers(
+            const DisplaySettings& display, const std::vector<const LayerSettings*>& layers,
+            const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache,
+            base::unique_fd&& bufferFence);
 
     // Clean-up method that should be called on the main thread after the
     // drawFence returned by drawLayers fires. This method will free up
@@ -232,6 +228,12 @@
     friend class threaded::RenderEngineThreaded;
     friend class RenderEngineTest_cleanupPostRender_cleansUpOnce_Test;
     const RenderEngineType mRenderEngineType;
+
+    virtual void drawLayersInternal(
+            const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+            const DisplaySettings& display, const std::vector<const LayerSettings*>& layers,
+            const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache,
+            base::unique_fd&& bufferFence) = 0;
 };
 
 struct RenderEngineCreationArgs {
@@ -318,6 +320,13 @@
             RenderEngine::RenderEngineType::SKIA_GL_THREADED;
 };
 
+struct RenderEngineResult {
+    // status indicates if drawing is successful
+    status_t status;
+    // drawFence will fire when the buffer has been drawn to and is ready to be examined.
+    base::unique_fd drawFence;
+};
+
 } // namespace renderengine
 } // namespace android
 
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index 0be3ba6..a7e6809 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -47,10 +47,15 @@
     MOCK_METHOD1(useProtectedContext, void(bool));
     MOCK_METHOD0(cleanupPostRender, void());
     MOCK_CONST_METHOD0(canSkipPostRenderCleanup, bool());
-    MOCK_METHOD6(drawLayers,
-                 status_t(const DisplaySettings&, const std::vector<const LayerSettings*>&,
-                          const std::shared_ptr<ExternalTexture>&, const bool, base::unique_fd&&,
-                          base::unique_fd*));
+    MOCK_METHOD5(drawLayers,
+                 std::future<RenderEngineResult>(const DisplaySettings&,
+                                                 const std::vector<const LayerSettings*>&,
+                                                 const std::shared_ptr<ExternalTexture>&,
+                                                 const bool, base::unique_fd&&));
+    MOCK_METHOD6(drawLayersInternal,
+                 void(const std::shared_ptr<std::promise<RenderEngineResult>>&&,
+                      const DisplaySettings&, const std::vector<const LayerSettings*>&,
+                      const std::shared_ptr<ExternalTexture>&, const bool, base::unique_fd&&));
     MOCK_METHOD0(cleanFramebufferCache, void());
     MOCK_METHOD0(getContextPriority, int());
     MOCK_METHOD0(supportsBackgroundBlur, bool());
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index 01df6a6..e796f66 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -95,7 +95,6 @@
             .alpha = 1,
     };
 
-    base::unique_fd drawFence;
     auto layers = std::vector<const LayerSettings*>{&layer, &caster};
     // Four combinations of settings are used (two transforms here, and drawShadowLayers is
     // called with two different destination data spaces) They're all rounded rect.
@@ -116,7 +115,7 @@
         layer.geometry.positionTransform = transform;
         caster.geometry.positionTransform = transform;
         renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
-                                base::unique_fd(), &drawFence);
+                                 base::unique_fd());
     }
 }
 
@@ -141,7 +140,6 @@
                                           }},
     };
 
-    base::unique_fd drawFence;
     auto layers = std::vector<const LayerSettings*>{&layer};
     for (auto dataspace : {kDestDataSpace, kOtherDataSpace}) {
         layer.sourceDataspace = dataspace;
@@ -155,7 +153,7 @@
                 for (auto alpha : {half(.2f), half(1.0f)}) {
                     layer.alpha = alpha;
                     renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
-                                             base::unique_fd(), &drawFence);
+                                             base::unique_fd());
                 }
             }
         }
@@ -178,14 +176,13 @@
             .alpha = 0.5,
     };
 
-    base::unique_fd drawFence;
     auto layers = std::vector<const LayerSettings*>{&layer};
     for (auto transform : {mat4(), kScaleAndTranslate}) {
         layer.geometry.positionTransform = transform;
         for (float roundedCornersRadius : {0.0f, 50.f}) {
             layer.geometry.roundedCornersRadius = roundedCornersRadius;
             renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
-                                     base::unique_fd(), &drawFence);
+                                     base::unique_fd());
         }
     }
 }
@@ -204,13 +201,12 @@
             .skipContentDraw = true,
     };
 
-    base::unique_fd drawFence;
     auto layers = std::vector<const LayerSettings*>{&layer};
     // Different blur code is invoked for radii less and greater than 30 pixels
     for (int radius : {9, 60}) {
         layer.backgroundBlurRadius = radius;
         renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
-                                 base::unique_fd(), &drawFence);
+                                 base::unique_fd());
     }
 }
 
@@ -246,7 +242,6 @@
                     },
     };
 
-    base::unique_fd drawFence;
     auto layers = std::vector<const LayerSettings*>{&layer};
     for (auto pixelSource : {bufferSource, bufferOpaque, colorSource}) {
         layer.source = pixelSource;
@@ -258,7 +253,7 @@
                 for (float alpha : {0.5f, 1.f}) {
                     layer.alpha = alpha,
                     renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
-                                             base::unique_fd(), &drawFence);
+                                             base::unique_fd());
                 }
             }
         }
@@ -294,10 +289,8 @@
 
     };
 
-    base::unique_fd drawFence;
     auto layers = std::vector<const LayerSettings*>{&layer};
-    renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
-                             base::unique_fd(), &drawFence);
+    renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, base::unique_fd());
 }
 
 static void drawHolePunchLayer(SkiaRenderEngine* renderengine, const DisplaySettings& display,
@@ -324,10 +317,8 @@
 
     };
 
-    base::unique_fd drawFence;
     auto layers = std::vector<const LayerSettings*>{&layer};
-    renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
-                            base::unique_fd(), &drawFence);
+    renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, base::unique_fd());
 }
 
 //
@@ -436,8 +427,10 @@
                 .source = PixelSource{.solidColor = half3(0.f, 0.f, 0.f)},
         };
         auto layers = std::vector<const LayerSettings*>{&layer};
-        renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
-                             base::unique_fd(), nullptr); // null drawFence makes it synchronous
+        // call get() to make it synchronous
+        renderengine
+                ->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, base::unique_fd())
+                .get();
 
         const nsecs_t timeAfter = systemTime();
         const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 9fbbdc3..052c61f 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -713,17 +713,18 @@
     return roundedRect;
 }
 
-status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
-                                        const std::vector<const LayerSettings*>& layers,
-                                        const std::shared_ptr<ExternalTexture>& buffer,
-                                        const bool /*useFramebufferCache*/,
-                                        base::unique_fd&& bufferFence, base::unique_fd* drawFence) {
+void SkiaGLRenderEngine::drawLayersInternal(
+        const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+        const DisplaySettings& display, const std::vector<const LayerSettings*>& layers,
+        const std::shared_ptr<ExternalTexture>& buffer, const bool /*useFramebufferCache*/,
+        base::unique_fd&& bufferFence) {
     ATRACE_NAME("SkiaGL::drawLayers");
 
     std::lock_guard<std::mutex> lock(mRenderingMutex);
     if (layers.empty()) {
         ALOGV("Drawing empty layer stack");
-        return NO_ERROR;
+        resultPromise->set_value({NO_ERROR, base::unique_fd()});
+        return;
     }
 
     if (bufferFence.get() >= 0) {
@@ -736,7 +737,8 @@
     }
     if (buffer == nullptr) {
         ALOGE("No output buffer provided. Aborting GPU composition.");
-        return BAD_VALUE;
+        resultPromise->set_value({BAD_VALUE, base::unique_fd()});
+        return;
     }
 
     validateOutputBufferUsage(buffer->getBuffer());
@@ -765,7 +767,8 @@
     SkCanvas* dstCanvas = mCapture->tryCapture(dstSurface.get());
     if (dstCanvas == nullptr) {
         ALOGE("Cannot acquire canvas from Skia.");
-        return BAD_VALUE;
+        resultPromise->set_value({BAD_VALUE, base::unique_fd()});
+        return;
     }
 
     // setup color filter if necessary
@@ -1094,13 +1097,11 @@
         activeSurface->flush();
     }
 
-    if (drawFence != nullptr) {
-        *drawFence = flush();
-    }
+    base::unique_fd drawFence = flush();
 
     // If flush failed or we don't support native fences, we need to force the
     // gl command stream to be executed.
-    bool requireSync = drawFence == nullptr || drawFence->get() < 0;
+    bool requireSync = drawFence.get() < 0;
     if (requireSync) {
         ATRACE_BEGIN("Submit(sync=true)");
     } else {
@@ -1112,11 +1113,13 @@
         ALOGE("Failed to flush RenderEngine commands");
         // Chances are, something illegal happened (either the caller passed
         // us bad parameters, or we messed up our shader generation).
-        return INVALID_OPERATION;
+        resultPromise->set_value({INVALID_OPERATION, std::move(drawFence)});
+        return;
     }
 
     // checkErrors();
-    return NO_ERROR;
+    resultPromise->set_value({NO_ERROR, std::move(drawFence)});
+    return;
 }
 
 inline SkRect SkiaGLRenderEngine::getSkRect(const FloatRect& rect) {
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index cc91948..0b46705 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -54,11 +54,6 @@
     ~SkiaGLRenderEngine() override EXCLUDES(mRenderingMutex);
 
     std::future<void> primeCache() override;
-    status_t drawLayers(const DisplaySettings& display,
-                        const std::vector<const LayerSettings*>& layers,
-                        const std::shared_ptr<ExternalTexture>& buffer,
-                        const bool useFramebufferCache, base::unique_fd&& bufferFence,
-                        base::unique_fd* drawFence) override;
     void cleanupPostRender() override;
     void cleanFramebufferCache() override{};
     int getContextPriority() override;
@@ -77,6 +72,11 @@
     void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) override;
     void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
     bool canSkipPostRenderCleanup() const override;
+    void drawLayersInternal(const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+                            const DisplaySettings& display,
+                            const std::vector<const LayerSettings*>& layers,
+                            const std::shared_ptr<ExternalTexture>& buffer,
+                            const bool useFramebufferCache, base::unique_fd&& bufferFence) override;
 
 private:
     static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index 7cd9eca..f61653b 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -44,14 +44,6 @@
     virtual void deleteTextures(size_t /*count*/, uint32_t const* /*names*/) override{};
     virtual bool isProtected() const override { return false; } // mInProtectedContext; }
     virtual bool supportsProtectedContent() const override { return false; };
-    virtual status_t drawLayers(const DisplaySettings& /*display*/,
-                                const std::vector<const LayerSettings*>& /*layers*/,
-                                const std::shared_ptr<ExternalTexture>& /*buffer*/,
-                                const bool /*useFramebufferCache*/,
-                                base::unique_fd&& /*bufferFence*/,
-                                base::unique_fd* /*drawFence*/) override {
-        return 0;
-    };
     virtual int getContextPriority() override { return 0; }
     virtual void assertShadersCompiled(int numShaders) {}
     virtual int reportShadersCompiled() { return 0; }
@@ -60,6 +52,14 @@
     virtual void mapExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/,
                                           bool /*isRenderable*/) override = 0;
     virtual void unmapExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/) override = 0;
+
+    virtual void drawLayersInternal(
+            const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+            const DisplaySettings& display, const std::vector<const LayerSettings*>& layers,
+            const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache,
+            base::unique_fd&& bufferFence) override {
+        resultPromise->set_value({NO_ERROR, base::unique_fd()});
+    };
 };
 
 } // namespace skia
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 33053a0..b3b726d 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -419,9 +419,10 @@
 
     void invokeDraw(renderengine::DisplaySettings settings,
                     std::vector<const renderengine::LayerSettings*> layers) {
-        base::unique_fd fence;
-        status_t status =
-                mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fence);
+        std::future<renderengine::RenderEngineResult> result =
+                mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd());
+        ASSERT_TRUE(result.valid());
+        auto [status, fence] = result.get();
 
         int fd = fence.release();
         if (fd >= 0) {
@@ -1312,9 +1313,11 @@
     layer.geometry.boundaries = fullscreenRect().toFloatRect();
     BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
     layers.push_back(&layer);
-    base::unique_fd fence;
-    status_t status = mRE->drawLayers(settings, layers, nullptr, true, base::unique_fd(), &fence);
+    std::future<renderengine::RenderEngineResult> result =
+            mRE->drawLayers(settings, layers, nullptr, true, base::unique_fd());
 
+    ASSERT_TRUE(result.valid());
+    auto [status, _] = result.get();
     ASSERT_EQ(BAD_VALUE, status);
 }
 
@@ -1333,7 +1336,10 @@
     layer.alpha = 1.0;
     layers.push_back(&layer);
 
-    status_t status = mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), nullptr);
+    std::future<renderengine::RenderEngineResult> result =
+            mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd());
+    ASSERT_TRUE(result.valid());
+    auto [status, _] = result.get();
     ASSERT_EQ(NO_ERROR, status);
     expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
 }
@@ -1360,7 +1366,10 @@
     layer.alpha = 1.0;
     layers.push_back(&layer);
 
-    status_t status = mRE->drawLayers(settings, layers, mBuffer, false, base::unique_fd(), nullptr);
+    std::future<renderengine::RenderEngineResult> result =
+            mRE->drawLayers(settings, layers, mBuffer, false, base::unique_fd());
+    ASSERT_TRUE(result.valid());
+    auto [status, _] = result.get();
     ASSERT_EQ(NO_ERROR, status);
     ASSERT_FALSE(mGLESRE->isFramebufferImageCachedForTesting(mBuffer->getBuffer()->getId()));
     expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
@@ -1760,10 +1769,17 @@
     layer.alpha = 1.0;
     layers.push_back(&layer);
 
-    base::unique_fd fenceOne;
-    mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fenceOne);
-    base::unique_fd fenceTwo;
-    mRE->drawLayers(settings, layers, mBuffer, true, std::move(fenceOne), &fenceTwo);
+    std::future<renderengine::RenderEngineResult> resultOne =
+            mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd());
+    ASSERT_TRUE(resultOne.valid());
+    auto [statusOne, fenceOne] = resultOne.get();
+    ASSERT_EQ(NO_ERROR, statusOne);
+
+    std::future<renderengine::RenderEngineResult> resultTwo =
+            mRE->drawLayers(settings, layers, mBuffer, true, std::move(fenceOne));
+    ASSERT_TRUE(resultTwo.valid());
+    auto [statusTwo, fenceTwo] = resultTwo.get();
+    ASSERT_EQ(NO_ERROR, statusTwo);
 
     const int fd = fenceTwo.get();
     if (fd >= 0) {
diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
index 830f463..99250c1 100644
--- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp
+++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
@@ -169,6 +169,7 @@
     status_t result = mThreadedRE->supportsBackgroundBlur();
     ASSERT_EQ(true, result);
 }
+
 TEST_F(RenderEngineThreadedTest, drawLayers) {
     renderengine::DisplaySettings settings;
     std::vector<const renderengine::LayerSettings*> layers;
@@ -177,17 +178,22 @@
                                            renderengine::ExternalTexture::Usage::READABLE |
                                                    renderengine::ExternalTexture::Usage::WRITEABLE);
     base::unique_fd bufferFence;
-    base::unique_fd drawFence;
 
-    EXPECT_CALL(*mRenderEngine, drawLayers)
-            .WillOnce([](const renderengine::DisplaySettings&,
-                         const std::vector<const renderengine::LayerSettings*>&,
-                         const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                         base::unique_fd&&, base::unique_fd*) -> status_t { return NO_ERROR; });
+    EXPECT_CALL(*mRenderEngine, drawLayersInternal)
+            .WillOnce([&](const std::shared_ptr<std::promise<renderengine::RenderEngineResult>>&&
+                                  resultPromise,
+                          const renderengine::DisplaySettings&,
+                          const std::vector<const renderengine::LayerSettings*>&,
+                          const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                          base::unique_fd &&) -> void {
+                resultPromise->set_value({NO_ERROR, base::unique_fd()});
+            });
 
-    status_t result = mThreadedRE->drawLayers(settings, layers, buffer, false,
-                                              std::move(bufferFence), &drawFence);
-    ASSERT_EQ(NO_ERROR, result);
+    std::future<renderengine::RenderEngineResult> result =
+            mThreadedRE->drawLayers(settings, layers, buffer, false, std::move(bufferFence));
+    ASSERT_TRUE(result.valid());
+    auto [status, _] = result.get();
+    ASSERT_EQ(NO_ERROR, status);
 }
 
 } // namespace android
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index 8e666d5..9a8201f 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -304,27 +304,33 @@
     return mRenderEngine->canSkipPostRenderCleanup();
 }
 
-status_t RenderEngineThreaded::drawLayers(const DisplaySettings& display,
-                                          const std::vector<const LayerSettings*>& layers,
-                                          const std::shared_ptr<ExternalTexture>& buffer,
-                                          const bool useFramebufferCache,
-                                          base::unique_fd&& bufferFence,
-                                          base::unique_fd* drawFence) {
+void RenderEngineThreaded::drawLayersInternal(
+        const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+        const DisplaySettings& display, const std::vector<const LayerSettings*>& layers,
+        const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache,
+        base::unique_fd&& bufferFence) {
+    resultPromise->set_value({NO_ERROR, base::unique_fd()});
+    return;
+}
+
+std::future<RenderEngineResult> RenderEngineThreaded::drawLayers(
+        const DisplaySettings& display, const std::vector<const LayerSettings*>& layers,
+        const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache,
+        base::unique_fd&& bufferFence) {
     ATRACE_CALL();
-    std::promise<status_t> resultPromise;
-    std::future<status_t> resultFuture = resultPromise.get_future();
+    const auto resultPromise = std::make_shared<std::promise<RenderEngineResult>>();
+    std::future<RenderEngineResult> resultFuture = resultPromise->get_future();
     {
         std::lock_guard lock(mThreadMutex);
-        mFunctionCalls.push([&resultPromise, &display, &layers, &buffer, useFramebufferCache,
-                             &bufferFence, &drawFence](renderengine::RenderEngine& instance) {
+        mFunctionCalls.push([resultPromise, &display, &layers, &buffer, useFramebufferCache,
+                             &bufferFence](renderengine::RenderEngine& instance) {
             ATRACE_NAME("REThreaded::drawLayers");
-            status_t status = instance.drawLayers(display, layers, buffer, useFramebufferCache,
-                                                  std::move(bufferFence), drawFence);
-            resultPromise.set_value(status);
+            instance.drawLayersInternal(std::move(resultPromise), display, layers, buffer,
+                                        useFramebufferCache, std::move(bufferFence));
         });
     }
     mCondition.notify_one();
-    return resultFuture.get();
+    return resultFuture;
 }
 
 void RenderEngineThreaded::cleanFramebufferCache() {
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index b197df7..2303caa 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -56,11 +56,11 @@
     void useProtectedContext(bool useProtectedContext) override;
     void cleanupPostRender() override;
 
-    status_t drawLayers(const DisplaySettings& display,
-                        const std::vector<const LayerSettings*>& layers,
-                        const std::shared_ptr<ExternalTexture>& buffer,
-                        const bool useFramebufferCache, base::unique_fd&& bufferFence,
-                        base::unique_fd* drawFence) override;
+    std::future<RenderEngineResult> drawLayers(const DisplaySettings& display,
+                                               const std::vector<const LayerSettings*>& layers,
+                                               const std::shared_ptr<ExternalTexture>& buffer,
+                                               const bool useFramebufferCache,
+                                               base::unique_fd&& bufferFence) override;
 
     void cleanFramebufferCache() override;
     int getContextPriority() override;
@@ -71,6 +71,11 @@
     void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) override;
     void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
     bool canSkipPostRenderCleanup() const override;
+    void drawLayersInternal(const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+                            const DisplaySettings& display,
+                            const std::vector<const LayerSettings*>& layers,
+                            const std::shared_ptr<ExternalTexture>& buffer,
+                            const bool useFramebufferCache, base::unique_fd&& bufferFence) override;
 
 private:
     void threadMain(CreateInstanceFactory factory);
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 0ff5eca..1747294 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -1030,10 +1030,9 @@
         }
     }
 
-    base::unique_fd readyFence;
     if (!hasClientComposition) {
         setExpensiveRenderingExpected(false);
-        return readyFence;
+        return base::unique_fd();
     }
 
     ALOGV("hasClientComposition");
@@ -1073,7 +1072,7 @@
                                                    clientCompositionLayers)) {
             outputCompositionState.reusedClientComposition = true;
             setExpensiveRenderingExpected(false);
-            return readyFence;
+            return base::unique_fd();
         }
         mClientCompositionRequestCache->add(tex->getBuffer()->getId(), clientCompositionDisplay,
                                             clientCompositionLayers);
@@ -1107,9 +1106,11 @@
     // probably to encapsulate the output buffer into a structure that dispatches resource cleanup
     // over to RenderEngine, in which case this flag can be removed from the drawLayers interface.
     const bool useFramebufferCache = outputState.layerFilter.toInternalDisplay;
-    status_t status =
-            renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, tex,
-                                    useFramebufferCache, std::move(fd), &readyFence);
+    auto [status, drawFence] =
+            renderEngine
+                    .drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, tex,
+                                useFramebufferCache, std::move(fd))
+                    .get();
 
     if (status != NO_ERROR && mClientCompositionRequestCache) {
         // If rendering was not successful, remove the request from the cache.
@@ -1117,15 +1118,15 @@
     }
 
     auto& timeStats = getCompositionEngine().getTimeStats();
-    if (readyFence.get() < 0) {
+    if (drawFence.get() < 0) {
         timeStats.recordRenderEngineDuration(renderEngineStart, systemTime());
     } else {
         timeStats.recordRenderEngineDuration(renderEngineStart,
                                              std::make_shared<FenceTime>(
-                                                     new Fence(dup(readyFence.get()))));
+                                                     new Fence(dup(drawFence.get()))));
     }
 
-    return readyFence;
+    return std::move(drawFence);
 }
 
 std::vector<LayerFE::LayerSettings> Output::generateClientCompositionRequests(
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index ccacdfb..5bbd58e 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -272,12 +272,12 @@
         bufferFence.reset(texture->getReadyFence()->dup());
     }
 
-    base::unique_fd drawFence;
-    status_t result =
-            renderEngine.drawLayers(displaySettings, layerSettingsPointers, texture->get(), false,
-                                    std::move(bufferFence), &drawFence);
+    auto [status, drawFence] = renderEngine
+                                       .drawLayers(displaySettings, layerSettingsPointers,
+                                                   texture->get(), false, std::move(bufferFence))
+                                       .get();
 
-    if (result == NO_ERROR) {
+    if (status == NO_ERROR) {
         mDrawFence = new Fence(drawFence.release());
         mOutputSpace = outputState.framebufferSpace;
         mTexture = texture;
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 6c510eb..a904c7d 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -35,6 +35,7 @@
 #include "CallOrderStateMachineHelper.h"
 #include "MockHWC2.h"
 #include "RegionMatcher.h"
+#include "TestUtils.h"
 #include "renderengine/ExternalTexture.h"
 
 namespace android::compositionengine {
@@ -3119,9 +3120,14 @@
             .WillRepeatedly(Return());
 
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, IsEmpty(), _, false, _, _))
-            .WillRepeatedly(Return(NO_ERROR));
-
+    EXPECT_CALL(mRenderEngine, drawLayers(_, IsEmpty(), _, false, _))
+            .WillRepeatedly([&](const renderengine::DisplaySettings&,
+                                const std::vector<const renderengine::LayerSettings*>&,
+                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                                base::unique_fd &&)
+                                    -> std::future<renderengine::RenderEngineResult> {
+                return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+            });
     verify().execute().expectAFenceWasReturned();
 }
 
@@ -3146,8 +3152,14 @@
                     }));
 
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _))
-            .WillRepeatedly(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _))
+            .WillRepeatedly([&](const renderengine::DisplaySettings&,
+                                const std::vector<const renderengine::LayerSettings*>&,
+                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                                base::unique_fd &&)
+                                    -> std::future<renderengine::RenderEngineResult> {
+                return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+            });
 
     verify().execute().expectAFenceWasReturned();
 }
@@ -3175,8 +3187,14 @@
                     }));
 
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, true, _, _))
-            .WillRepeatedly(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, true, _))
+            .WillRepeatedly([&](const renderengine::DisplaySettings&,
+                                const std::vector<const renderengine::LayerSettings*>&,
+                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                                base::unique_fd &&)
+                                    -> std::future<renderengine::RenderEngineResult> {
+                return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+            });
 
     verify().execute().expectAFenceWasReturned();
 }
@@ -3199,9 +3217,12 @@
             .WillRepeatedly(Return());
 
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _))
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _))
             .Times(2)
-            .WillOnce(Return(NO_ERROR));
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
 
     verify().execute().expectAFenceWasReturned();
     EXPECT_FALSE(mOutput.mState.reusedClientComposition);
@@ -3228,8 +3249,9 @@
             .WillRepeatedly(Return());
 
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _))
-            .WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
     EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false));
 
     verify().execute().expectAFenceWasReturned();
@@ -3263,8 +3285,14 @@
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_))
             .WillOnce(Return(mOutputBuffer))
             .WillOnce(Return(otherOutputBuffer));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _))
-            .WillRepeatedly(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _))
+            .WillRepeatedly([&](const renderengine::DisplaySettings&,
+                                const std::vector<const renderengine::LayerSettings*>&,
+                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                                base::unique_fd &&)
+                                    -> std::future<renderengine::RenderEngineResult> {
+                return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+            });
 
     verify().execute().expectAFenceWasReturned();
     EXPECT_FALSE(mOutput.mState.reusedClientComposition);
@@ -3293,10 +3321,12 @@
             .WillRepeatedly(Return());
 
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _))
-            .WillOnce(Return(NO_ERROR));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r3)), _, false, _, _))
-            .WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r3)), _, false, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
 
     verify().execute().expectAFenceWasReturned();
     EXPECT_FALSE(mOutput.mState.reusedClientComposition);
@@ -3345,8 +3375,9 @@
     struct ExpectDisplaySettingsState
           : public CallOrderStateMachineHelper<TestType, ExpectDisplaySettingsState> {
         auto thenExpectDisplaySettingsUsed(renderengine::DisplaySettings settings) {
-            EXPECT_CALL(getInstance()->mRenderEngine, drawLayers(settings, _, _, false, _, _))
-                    .WillOnce(Return(NO_ERROR));
+            EXPECT_CALL(getInstance()->mRenderEngine, drawLayers(settings, _, _, false, _))
+                    .WillOnce(Return(ByMove(futureOf<renderengine::RenderEngineResult>(
+                            {NO_ERROR, base::unique_fd()}))));
             return nextState<ExecuteState>();
         }
     };
@@ -3444,8 +3475,15 @@
         EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
                 .WillRepeatedly(Return());
         EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
-        EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _))
-                .WillRepeatedly(Return(NO_ERROR));
+        EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _))
+                .WillRepeatedly(
+                        [&](const renderengine::DisplaySettings&,
+                            const std::vector<const renderengine::LayerSettings*>&,
+                            const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                            base::unique_fd &&) -> std::future<renderengine::RenderEngineResult> {
+                            return futureOf<renderengine::RenderEngineResult>(
+                                    {NO_ERROR, base::unique_fd()});
+                        });
     }
 
     Layer mLayer1;
@@ -3497,7 +3535,9 @@
     EXPECT_CALL(*mRenderSurface, setProtected(true));
     // Must happen after setting the protected content state.
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
 
     mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
 }
@@ -3567,7 +3607,9 @@
     InSequence seq;
 
     EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
 
     mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
 }
@@ -3584,7 +3626,9 @@
                                     /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
         EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace))
                 .WillOnce(Return(std::vector<LayerFE::LayerSettings>{}));
-        EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _)).WillOnce(Return(NO_ERROR));
+        EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _))
+                .WillOnce(Return(ByMove(futureOf<renderengine::RenderEngineResult>(
+                        {NO_ERROR, base::unique_fd()}))));
         EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u));
         EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
                 .WillRepeatedly(Return(&mLayer.outputLayer));
diff --git a/services/surfaceflinger/CompositionEngine/tests/TestUtils.h b/services/surfaceflinger/CompositionEngine/tests/TestUtils.h
new file mode 100644
index 0000000..c80fde6
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/TestUtils.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 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 <future>
+
+namespace android::compositionengine {
+namespace {
+
+template <class T>
+std::future<T> futureOf(T obj) {
+    std::promise<T> resultPromise;
+    std::future<T> resultFuture = resultPromise.get_future();
+    resultPromise.set_value(std::move(obj));
+    return resultFuture;
+}
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index ec81322..818334d 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -27,6 +27,8 @@
 #include <utils/Errors.h>
 #include <memory>
 
+#include "tests/TestUtils.h"
+
 namespace android::compositionengine {
 using namespace std::chrono_literals;
 
@@ -342,10 +344,11 @@
     clientCompList2.push_back({});
     clientCompList2[0].alpha = 0.75f;
 
-    const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings,
-                                const std::vector<const renderengine::LayerSettings*>& layers,
-                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                                base::unique_fd&&, base::unique_fd*) -> size_t {
+    const auto drawLayers =
+            [&](const renderengine::DisplaySettings& displaySettings,
+                const std::vector<const renderengine::LayerSettings*>& layers,
+                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                base::unique_fd &&) -> std::future<renderengine::RenderEngineResult> {
         EXPECT_EQ(mOutputState.framebufferSpace.content, displaySettings.physicalDisplay);
         EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip);
         EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation),
@@ -353,8 +356,7 @@
         EXPECT_EQ(0.5f, layers[0]->alpha);
         EXPECT_EQ(0.75f, layers[1]->alpha);
         EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace);
-
-        return NO_ERROR;
+        return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
     };
 
     EXPECT_CALL(*layerFE1,
@@ -363,7 +365,7 @@
     EXPECT_CALL(*layerFE2,
                 prepareClientCompositionList(ClientCompositionTargetSettingsSecureEq(false)))
             .WillOnce(Return(clientCompList2));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
     mOutputState.isSecure = false;
     cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
     expectReadyBuffer(cachedSet);
@@ -394,10 +396,11 @@
     clientCompList2.push_back({});
     clientCompList2[0].alpha = 0.75f;
 
-    const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings,
-                                const std::vector<const renderengine::LayerSettings*>& layers,
-                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                                base::unique_fd&&, base::unique_fd*) -> size_t {
+    const auto drawLayers =
+            [&](const renderengine::DisplaySettings& displaySettings,
+                const std::vector<const renderengine::LayerSettings*>& layers,
+                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                base::unique_fd &&) -> std::future<renderengine::RenderEngineResult> {
         EXPECT_EQ(mOutputState.framebufferSpace.content, displaySettings.physicalDisplay);
         EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip);
         EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation),
@@ -406,7 +409,7 @@
         EXPECT_EQ(0.75f, layers[1]->alpha);
         EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace);
 
-        return NO_ERROR;
+        return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
     };
 
     EXPECT_CALL(*layerFE1,
@@ -415,7 +418,7 @@
     EXPECT_CALL(*layerFE2,
                 prepareClientCompositionList(ClientCompositionTargetSettingsSecureEq(true)))
             .WillOnce(Return(clientCompList2));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
     mOutputState.isSecure = true;
     cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
     expectReadyBuffer(cachedSet);
@@ -448,10 +451,11 @@
 
     mOutputState.framebufferSpace = ProjectionSpace(ui::Size(10, 20), Rect(2, 3, 10, 5));
 
-    const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings,
-                                const std::vector<const renderengine::LayerSettings*>& layers,
-                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                                base::unique_fd&&, base::unique_fd*) -> size_t {
+    const auto drawLayers =
+            [&](const renderengine::DisplaySettings& displaySettings,
+                const std::vector<const renderengine::LayerSettings*>& layers,
+                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                base::unique_fd &&) -> std::future<renderengine::RenderEngineResult> {
         EXPECT_EQ(mOutputState.framebufferSpace.content, displaySettings.physicalDisplay);
         EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip);
         EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation),
@@ -460,12 +464,12 @@
         EXPECT_EQ(0.75f, layers[1]->alpha);
         EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace);
 
-        return NO_ERROR;
+        return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
     };
 
     EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1));
     EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
     cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
     expectReadyBuffer(cachedSet);
 
@@ -650,10 +654,11 @@
     EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
     EXPECT_CALL(*layerFE3, prepareClientCompositionList(_)).WillOnce(Return(clientCompList3));
 
-    const auto drawLayers = [&](const renderengine::DisplaySettings&,
-                                const std::vector<const renderengine::LayerSettings*>& layers,
-                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                                base::unique_fd&&, base::unique_fd*) -> size_t {
+    const auto drawLayers =
+            [&](const renderengine::DisplaySettings&,
+                const std::vector<const renderengine::LayerSettings*>& layers,
+                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                base::unique_fd &&) -> std::future<renderengine::RenderEngineResult> {
         // If the highlight layer is enabled, it will increase the size by 1.
         // We're interested in the third layer either way.
         EXPECT_GE(layers.size(), 4u);
@@ -673,10 +678,10 @@
             EXPECT_EQ(1.0f, holePunchBackgroundSettings->alpha);
         }
 
-        return NO_ERROR;
+        return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
     };
 
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
     cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
 }
 
@@ -710,10 +715,11 @@
     EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
     EXPECT_CALL(*layerFE3, prepareClientCompositionList(_)).WillOnce(Return(clientCompList3));
 
-    const auto drawLayers = [&](const renderengine::DisplaySettings&,
-                                const std::vector<const renderengine::LayerSettings*>& layers,
-                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                                base::unique_fd&&, base::unique_fd*) -> size_t {
+    const auto drawLayers =
+            [&](const renderengine::DisplaySettings&,
+                const std::vector<const renderengine::LayerSettings*>& layers,
+                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                base::unique_fd &&) -> std::future<renderengine::RenderEngineResult> {
         // If the highlight layer is enabled, it will increase the size by 1.
         // We're interested in the third layer either way.
         EXPECT_GE(layers.size(), 4u);
@@ -734,10 +740,10 @@
             EXPECT_EQ(1.0f, holePunchBackgroundSettings->alpha);
         }
 
-        return NO_ERROR;
+        return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
     };
 
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
     cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
 }
 
@@ -859,10 +865,11 @@
                                 BackgroundBlurOnly)))
             .WillOnce(Return(clientCompList3));
 
-    const auto drawLayers = [&](const renderengine::DisplaySettings&,
-                                const std::vector<const renderengine::LayerSettings*>& layers,
-                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                                base::unique_fd&&, base::unique_fd*) -> int32_t {
+    const auto drawLayers =
+            [&](const renderengine::DisplaySettings&,
+                const std::vector<const renderengine::LayerSettings*>& layers,
+                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                base::unique_fd &&) -> std::future<renderengine::RenderEngineResult> {
         // If the highlight layer is enabled, it will increase the size by 1.
         // We're interested in the third layer either way.
         EXPECT_GE(layers.size(), 3u);
@@ -871,10 +878,10 @@
         EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), blurSettings->source.solidColor);
         EXPECT_EQ(0.0f, blurSettings->alpha);
 
-        return NO_ERROR;
+        return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
     };
 
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
     cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
 }
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index fc9d0dd..77d038d 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -26,6 +26,8 @@
 #include <renderengine/mock/RenderEngine.h>
 #include <chrono>
 
+#include "tests/TestUtils.h"
+
 namespace android::compositionengine {
 using namespace std::chrono_literals;
 using impl::planner::CachedSet;
@@ -166,7 +168,9 @@
 
 void FlattenerTest::expectAllLayersFlattened(const std::vector<const LayerState*>& layers) {
     // layers would be flattened but the buffer would not be overridden
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
 
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
@@ -400,7 +404,9 @@
     // caleed for Layer2 and Layer3
     layerState1->resetFramesSinceBufferUpdate();
 
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -422,7 +428,9 @@
     layerState1->incrementFramesSinceBufferUpdate();
     mTime += 200ms;
 
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -473,7 +481,9 @@
     // called for Layer1 and Layer2
     layerState3->resetFramesSinceBufferUpdate();
 
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -486,7 +496,9 @@
     EXPECT_EQ(nullptr, overrideBuffer5);
 
     // Layers 1 and 2 will be flattened a new drawFrame would be called for Layer4 and Layer5
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -514,8 +526,9 @@
 
     layerState3->incrementFramesSinceBufferUpdate();
     mTime += 200ms;
-
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -569,7 +582,9 @@
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
 
     // This will render a CachedSet.
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
     mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     // We've rendered a CachedSet, but we haven't merged it in.
@@ -579,7 +594,7 @@
 
     // This time we merge the CachedSet in, so we have a new hash, and we should
     // only have two sets.
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0);
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -631,7 +646,9 @@
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
 
     // This will render a CachedSet.
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
     mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     // We've rendered a CachedSet, but we haven't merged it in.
@@ -641,7 +658,7 @@
 
     // This time we merge the CachedSet in, so we have a new hash, and we should
     // only have two sets.
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0);
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -701,14 +718,16 @@
 
     // This will render a CachedSet of layer 0. Though it is just one layer, it satisfies the
     // exception that there would be a hole punch above it.
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
     mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     // We've rendered a CachedSet, but we haven't merged it in.
     EXPECT_EQ(nullptr, overrideBuffer0);
 
     // This time we merge the CachedSet in and we should still have only two sets.
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0);
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -751,7 +770,9 @@
     layerState3->resetFramesSinceBufferUpdate();
 
     // layers would be flattened but the buffer would not be overridden
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
 
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
@@ -795,7 +816,9 @@
     layerState1->resetFramesSinceBufferUpdate();
 
     // layers would be flattened but the buffer would not be overridden
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillRepeatedly(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillRepeatedly(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
 
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
@@ -847,7 +870,9 @@
     layerState1->resetFramesSinceBufferUpdate();
 
     // layers would be flattened but the buffer would not be overridden
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
 
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
@@ -894,7 +919,9 @@
     layerStateWithBlurBehind->resetFramesSinceBufferUpdate();
 
     // layers would be flattened but the buffer would not be overridden
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
 
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
@@ -935,7 +962,9 @@
     // Mark the layers inactive
     mTime += 200ms;
     // layers would be flattened but the buffer would not be overridden
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
 
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
@@ -947,7 +976,7 @@
 
     // Simulate attempting to render prior to merging the new cached set with the layer stack.
     // Here we should not try to re-render.
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0);
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
     mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     // We provide the override buffer now that it's rendered
@@ -992,13 +1021,15 @@
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
 
     for (size_t i = 0; i < kMaxDeferRenderAttempts; i++) {
-        EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0);
+        EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
         mFlattener->renderCachedSets(mOutputState,
                                      std::chrono::steady_clock::now() -
                                              (kCachedSetRenderDuration + 10ms));
     }
 
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
     mFlattener->renderCachedSets(mOutputState,
                                  std::chrono::steady_clock::now() -
                                          (kCachedSetRenderDuration + 10ms));
@@ -1032,7 +1063,9 @@
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
 
     // This will render a CachedSet.
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
     mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     // We've rendered a CachedSet, but we haven't merged it in.
@@ -1042,7 +1075,7 @@
 
     // This time we merge the CachedSet in, so we have a new hash, and we should
     // only have two sets.
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0);
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -1081,7 +1114,9 @@
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
 
     // This will render a CachedSet.
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
     mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     // We've rendered a CachedSet, but we haven't merged it in.
@@ -1091,7 +1126,7 @@
 
     // This time we merge the CachedSet in, so we have a new hash, and we should
     // only have two sets.
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0);
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -1130,7 +1165,9 @@
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
 
     // This will render a CachedSet.
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
     mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     // We've rendered a CachedSet, but we haven't merged it in.
@@ -1140,7 +1177,7 @@
 
     // This time we merge the CachedSet in, so we have a new hash, and we should
     // only have two sets.
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0);
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 5d8d79b..15db2ed 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -6328,12 +6328,14 @@
     // Use an empty fence for the buffer fence, since we just created the buffer so
     // there is no need for synchronization with the GPU.
     base::unique_fd bufferFence;
-    base::unique_fd drawFence;
     getRenderEngine().useProtectedContext(useProtected);
 
     const constexpr bool kUseFramebufferCache = false;
-    getRenderEngine().drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, buffer,
-                                 kUseFramebufferCache, std::move(bufferFence), &drawFence);
+    auto [status, drawFence] =
+            getRenderEngine()
+                    .drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, buffer,
+                                kUseFramebufferCache, std::move(bufferFence))
+                    .get();
 
     if (drawFence >= 0) {
         sp<Fence> releaseFence = new Fence(dup(drawFence));
@@ -6346,7 +6348,7 @@
     // Always switch back to unprotected context.
     getRenderEngine().useProtectedContext(false);
 
-    return NO_ERROR;
+    return status;
 }
 
 void SurfaceFlinger::windowInfosReported() {
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 93a4ce2..5135ff9 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -255,6 +255,14 @@
     LayerCase::cleanup(this);
 }
 
+template <class T>
+std::future<T> futureOf(T obj) {
+    std::promise<T> resultPromise;
+    std::future<T> resultFuture = resultPromise.get_future();
+    resultPromise.set_value(std::move(obj));
+    return resultFuture;
+}
+
 /* ------------------------------------------------------------------------
  * Variants for each display configuration which can be tested
  */
@@ -337,16 +345,18 @@
     template <typename Case>
     static void setupCommonScreensCaptureCallExpectations(CompositionTest* test) {
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
-                .WillRepeatedly([](const renderengine::DisplaySettings& displaySettings,
-                                   const std::vector<const renderengine::LayerSettings*>&,
-                                   const std::shared_ptr<renderengine::ExternalTexture>&,
-                                   const bool, base::unique_fd&&, base::unique_fd*) -> status_t {
+                .WillRepeatedly([&](const renderengine::DisplaySettings& displaySettings,
+                                    const std::vector<const renderengine::LayerSettings*>&,
+                                    const std::shared_ptr<renderengine::ExternalTexture>&,
+                                    const bool, base::unique_fd &&)
+                                        -> std::future<renderengine::RenderEngineResult> {
                     EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
                     EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
                               displaySettings.physicalDisplay);
                     EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
                               displaySettings.clip);
-                    return NO_ERROR;
+                    return futureOf<renderengine::RenderEngineResult>(
+                            {NO_ERROR, base::unique_fd()});
                 });
     }
 
@@ -386,17 +396,19 @@
                 .WillOnce(DoAll(SetArgPointee<0>(test->mNativeWindowBuffer), SetArgPointee<1>(-1),
                                 Return(0)));
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
-                .WillRepeatedly([](const renderengine::DisplaySettings& displaySettings,
-                                   const std::vector<const renderengine::LayerSettings*>&,
-                                   const std::shared_ptr<renderengine::ExternalTexture>&,
-                                   const bool, base::unique_fd&&, base::unique_fd*) -> status_t {
+                .WillRepeatedly([&](const renderengine::DisplaySettings& displaySettings,
+                                    const std::vector<const renderengine::LayerSettings*>&,
+                                    const std::shared_ptr<renderengine::ExternalTexture>&,
+                                    const bool, base::unique_fd &&)
+                                        -> std::future<renderengine::RenderEngineResult> {
                     EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
                     EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
                               displaySettings.physicalDisplay);
                     EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
                               displaySettings.clip);
                     EXPECT_EQ(ui::Dataspace::UNKNOWN, displaySettings.outputDataspace);
-                    return NO_ERROR;
+                    return futureOf<renderengine::RenderEngineResult>(
+                            {NO_ERROR, base::unique_fd()});
                 });
     }
 
@@ -620,10 +632,10 @@
 
     static void setupREBufferCompositionCommonCallExpectations(CompositionTest* test) {
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
-                .WillOnce([](const renderengine::DisplaySettings& displaySettings,
-                             const std::vector<const renderengine::LayerSettings*>& layerSettings,
-                             const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                             base::unique_fd&&, base::unique_fd*) -> status_t {
+                .WillOnce([&](const renderengine::DisplaySettings& displaySettings,
+                              const std::vector<const renderengine::LayerSettings*>& layerSettings,
+                              const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                              base::unique_fd &&) -> std::future<renderengine::RenderEngineResult> {
                     EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
                     EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
                               displaySettings.physicalDisplay);
@@ -631,11 +643,14 @@
                               displaySettings.clip);
                     // screen capture adds an additional color layer as an alpha
                     // prefill, so gtet the back layer.
+                    std::future<renderengine::RenderEngineResult> resultFuture =
+                            futureOf<renderengine::RenderEngineResult>(
+                                    {NO_ERROR, base::unique_fd()});
                     if (layerSettings.empty()) {
                         ADD_FAILURE() << "layerSettings was not expected to be empty in "
                                          "setupREBufferCompositionCommonCallExpectations "
                                          "verification lambda";
-                        return NO_ERROR;
+                        return resultFuture;
                     }
                     const renderengine::LayerSettings* layer = layerSettings.back();
                     EXPECT_THAT(layer->source.buffer.buffer, Not(IsNull()));
@@ -647,7 +662,7 @@
                     EXPECT_EQ(0.0, layer->geometry.roundedCornersRadius);
                     EXPECT_EQ(ui::Dataspace::UNKNOWN, layer->sourceDataspace);
                     EXPECT_EQ(LayerProperties::COLOR[3], layer->alpha);
-                    return NO_ERROR;
+                    return resultFuture;
                 });
     }
 
@@ -669,10 +684,10 @@
 
     static void setupREColorCompositionCallExpectations(CompositionTest* test) {
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
-                .WillOnce([](const renderengine::DisplaySettings& displaySettings,
-                             const std::vector<const renderengine::LayerSettings*>& layerSettings,
-                             const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                             base::unique_fd&&, base::unique_fd*) -> status_t {
+                .WillOnce([&](const renderengine::DisplaySettings& displaySettings,
+                              const std::vector<const renderengine::LayerSettings*>& layerSettings,
+                              const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                              base::unique_fd &&) -> std::future<renderengine::RenderEngineResult> {
                     EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
                     EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
                               displaySettings.physicalDisplay);
@@ -680,11 +695,14 @@
                               displaySettings.clip);
                     // screen capture adds an additional color layer as an alpha
                     // prefill, so get the back layer.
+                    std::future<renderengine::RenderEngineResult> resultFuture =
+                            futureOf<renderengine::RenderEngineResult>(
+                                    {NO_ERROR, base::unique_fd()});
                     if (layerSettings.empty()) {
                         ADD_FAILURE()
                                 << "layerSettings was not expected to be empty in "
                                    "setupREColorCompositionCallExpectations verification lambda";
-                        return NO_ERROR;
+                        return resultFuture;
                     }
                     const renderengine::LayerSettings* layer = layerSettings.back();
                     EXPECT_THAT(layer->source.buffer.buffer, IsNull());
@@ -694,7 +712,7 @@
                     EXPECT_EQ(0.0, layer->geometry.roundedCornersRadius);
                     EXPECT_EQ(ui::Dataspace::UNKNOWN, layer->sourceDataspace);
                     EXPECT_EQ(LayerProperties::COLOR[3], layer->alpha);
-                    return NO_ERROR;
+                    return resultFuture;
                 });
     }
 
@@ -746,10 +764,10 @@
 
     static void setupInsecureREBufferCompositionCommonCallExpectations(CompositionTest* test) {
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
-                .WillOnce([](const renderengine::DisplaySettings& displaySettings,
-                             const std::vector<const renderengine::LayerSettings*>& layerSettings,
-                             const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                             base::unique_fd&&, base::unique_fd*) -> status_t {
+                .WillOnce([&](const renderengine::DisplaySettings& displaySettings,
+                              const std::vector<const renderengine::LayerSettings*>& layerSettings,
+                              const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                              base::unique_fd &&) -> std::future<renderengine::RenderEngineResult> {
                     EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
                     EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
                               displaySettings.physicalDisplay);
@@ -757,11 +775,14 @@
                               displaySettings.clip);
                     // screen capture adds an additional color layer as an alpha
                     // prefill, so get the back layer.
+                    std::future<renderengine::RenderEngineResult> resultFuture =
+                            futureOf<renderengine::RenderEngineResult>(
+                                    {NO_ERROR, base::unique_fd()});
                     if (layerSettings.empty()) {
                         ADD_FAILURE() << "layerSettings was not expected to be empty in "
                                          "setupInsecureREBufferCompositionCommonCallExpectations "
                                          "verification lambda";
-                        return NO_ERROR;
+                        return resultFuture;
                     }
                     const renderengine::LayerSettings* layer = layerSettings.back();
                     EXPECT_THAT(layer->source.buffer.buffer, IsNull());
@@ -769,7 +790,7 @@
                     EXPECT_EQ(0.0, layer->geometry.roundedCornersRadius);
                     EXPECT_EQ(ui::Dataspace::UNKNOWN, layer->sourceDataspace);
                     EXPECT_EQ(1.0f, layer->alpha);
-                    return NO_ERROR;
+                    return resultFuture;
                 });
     }