Revert "Use renderengine::drawLayers api everywhere."

This reverts commit 0f7148365cfa405e8ed802b33c0e2a7b790fcf42.

Reason for revert: b/123878751, preparing while i investigate

Bug: 123878751
Test: bug no longer repros
Change-Id: I6c77427f0c113f96b68aec479ccec52b0bbb2c6b
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index c137394..c5a9942 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -449,7 +449,6 @@
 }
 
 base::unique_fd GLESRenderEngine::flush() {
-    ATRACE_CALL();
     if (!GLExtensions::getInstance().hasNativeFenceSync()) {
         return base::unique_fd();
     }
@@ -480,7 +479,6 @@
 }
 
 bool GLESRenderEngine::finish() {
-    ATRACE_CALL();
     if (!GLExtensions::getInstance().hasFenceSync()) {
         ALOGW("no synchronization support");
         return false;
@@ -596,7 +594,6 @@
 }
 
 void GLESRenderEngine::bindExternalTextureImage(uint32_t texName, const Image& image) {
-    ATRACE_CALL();
     const GLImage& glImage = static_cast<const GLImage&>(image);
     const GLenum target = GL_TEXTURE_EXTERNAL_OES;
 
@@ -611,15 +608,8 @@
 }
 
 status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer,
-                                                     sp<Fence> bufferFence, bool readCache) {
-    return bindExternalTextureBuffer(texName, buffer, bufferFence, readCache,
-                                     /*persistCache=*/false);
-}
-
-status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer,
                                                      sp<Fence> bufferFence, bool readCache,
                                                      bool persistCache) {
-    ATRACE_CALL();
     if (readCache) {
         auto cachedImage = mImageCache.find(buffer->getId());
 
@@ -665,7 +655,7 @@
     }
 
     // We don't always want to persist to the cache, e.g. on older devices we
-    // might bind for synchronization purposes, but that might leak if we never
+    // might bind for synchronization purpoeses, but that might leak if we never
     // call drawLayers again, so it's just better to recreate the image again
     // if needed when we draw.
     if (persistCache) {
@@ -713,7 +703,6 @@
 }
 
 status_t GLESRenderEngine::bindFrameBuffer(Framebuffer* framebuffer) {
-    ATRACE_CALL();
     GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(framebuffer);
     EGLImageKHR eglImage = glFramebuffer->getEGLImage();
     uint32_t textureName = glFramebuffer->getTextureName();
@@ -781,7 +770,6 @@
                                       const std::vector<LayerSettings>& layers,
                                       ANativeWindowBuffer* const buffer,
                                       base::unique_fd* drawFence) {
-    ATRACE_CALL();
     if (layers.empty()) {
         ALOGV("Drawing empty layer stack");
         return NO_ERROR;
@@ -798,13 +786,6 @@
 
     evictImages(layers);
 
-    // clear the entire buffer, sometimes when we reuse buffers we'd persist
-    // ghost images otherwise.
-    // we also require a full transparent framebuffer for overlays. This is
-    // probably not quite efficient on all GPUs, since we could filter out
-    // opaque layers.
-    clearWithColor(0.0, 0.0, 0.0, 0.0);
-
     setViewportAndProjection(display.physicalDisplay, display.clip);
 
     setOutputDataSpace(display.outputDataspace);
@@ -813,7 +794,6 @@
     mat4 projectionMatrix = mState.projectionMatrix * display.globalTransform;
     mState.projectionMatrix = projectionMatrix;
     if (!display.clearRegion.isEmpty()) {
-        glDisable(GL_BLEND);
         fillRegionWithColor(display.clearRegion, 0.0, 0.0, 0.0, 1.0);
     }
 
@@ -833,11 +813,9 @@
 
         bool usePremultipliedAlpha = true;
         bool disableTexture = true;
-        bool isOpaque = false;
 
         if (layer.source.buffer.buffer != nullptr) {
             disableTexture = false;
-            isOpaque = layer.source.buffer.isOpaque;
 
             sp<GraphicBuffer> gBuf = layer.source.buffer.buffer;
 
@@ -847,19 +825,17 @@
 
             usePremultipliedAlpha = layer.source.buffer.usePremultipliedAlpha;
             Texture texture(Texture::TEXTURE_EXTERNAL, layer.source.buffer.textureName);
-            mat4 texMatrix = layer.source.buffer.textureTransform;
-
-            texture.setMatrix(texMatrix.asArray());
+            texture.setMatrix(layer.source.buffer.textureTransform.asArray());
             texture.setFiltering(layer.source.buffer.useTextureFiltering);
 
             texture.setDimensions(gBuf->getWidth(), gBuf->getHeight());
             setSourceY410BT2020(layer.source.buffer.isY410BT2020);
 
             renderengine::Mesh::VertexArray<vec2> texCoords(mesh.getTexCoordArray<vec2>());
-            texCoords[0] = vec2(0.0, 0.0);
-            texCoords[1] = vec2(0.0, 1.0);
-            texCoords[2] = vec2(1.0, 1.0);
-            texCoords[3] = vec2(1.0, 0.0);
+            texCoords[0] = vec2(0.0, 1.0);
+            texCoords[1] = vec2(0.0, 0.0);
+            texCoords[2] = vec2(1.0, 0.0);
+            texCoords[3] = vec2(1.0, 1.0);
             setupLayerTexturing(texture);
         }
 
@@ -867,11 +843,8 @@
         const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha);
         // Buffer sources will have a black solid color ignored in the shader,
         // so in that scenario the solid color passed here is arbitrary.
-        setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color,
-                           layer.geometry.roundedCornersRadius);
-        if (layer.disableBlending) {
-            glDisable(GL_BLEND);
-        }
+        setupLayerBlending(usePremultipliedAlpha, layer.source.buffer.isOpaque, disableTexture,
+                           color, layer.geometry.roundedCornersRadius);
         setSourceDataSpace(layer.sourceDataspace);
 
         drawMesh(mesh);
@@ -930,7 +903,6 @@
 }
 
 void GLESRenderEngine::setViewportAndProjection(Rect viewport, Rect clip) {
-    ATRACE_CALL();
     mVpWidth = viewport.getWidth();
     mVpHeight = viewport.getHeight();
 
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 34187f1..e094860 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -71,8 +71,6 @@
     void genTextures(size_t count, uint32_t* names) override;
     void deleteTextures(size_t count, uint32_t const* names) override;
     void bindExternalTextureImage(uint32_t texName, const Image& image) override;
-    status_t bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer, sp<Fence> fence,
-                                       bool readCache);
     status_t bindFrameBuffer(Framebuffer* framebuffer) override;
     void unbindFrameBuffer(Framebuffer* framebuffer) override;
     void checkErrors() const override;
@@ -186,9 +184,6 @@
     const bool mUseColorManagement = false;
 
     // Cache of GL images that we'll store per GraphicBuffer ID
-    // TODO: Layer should call back on destruction instead to clean this up,
-    // as it has better system utilization at the potential expense of a
-    // more complicated interface.
     std::unordered_map<uint64_t, std::unique_ptr<Image>> mImageCache;
 
     class FlushTracer {
diff --git a/libs/renderengine/gl/GLFramebuffer.cpp b/libs/renderengine/gl/GLFramebuffer.cpp
index 0e3b405..4a519bb 100644
--- a/libs/renderengine/gl/GLFramebuffer.cpp
+++ b/libs/renderengine/gl/GLFramebuffer.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
 #include "GLFramebuffer.h"
 
 #include <GLES/gl.h>
@@ -23,7 +21,6 @@
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
 #include <nativebase/nativebase.h>
-#include <utils/Trace.h>
 #include "GLESRenderEngine.h"
 
 namespace android {
@@ -43,7 +40,6 @@
 }
 
 bool GLFramebuffer::setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected) {
-    ATRACE_CALL();
     if (mEGLImage != EGL_NO_IMAGE_KHR) {
         eglDestroyImageKHR(mEGLDisplay, mEGLImage);
         mEGLImage = EGL_NO_IMAGE_KHR;
diff --git a/libs/renderengine/gl/GLImage.cpp b/libs/renderengine/gl/GLImage.cpp
index 77e648e..587cb31 100644
--- a/libs/renderengine/gl/GLImage.cpp
+++ b/libs/renderengine/gl/GLImage.cpp
@@ -14,14 +14,11 @@
  * limitations under the License.
  */
 
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
 #include "GLImage.h"
 
 #include <vector>
 
 #include <log/log.h>
-#include <utils/Trace.h>
 #include "GLESRenderEngine.h"
 #include "GLExtensions.h"
 
@@ -53,7 +50,6 @@
 }
 
 bool GLImage::setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) {
-    ATRACE_CALL();
     if (mEGLImage != EGL_NO_IMAGE_KHR) {
         if (!eglDestroyImageKHR(mEGLDisplay, mEGLImage)) {
             ALOGE("failed to destroy image: %#x", eglGetError());
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index aa45ed8..56ac714 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -126,9 +126,6 @@
     // Additional layer-specific color transform to be applied before the global
     // transform.
     mat4 colorTransform = mat4();
-
-    // True if blending will be forced to be disabled.
-    bool disableBlending = false;
 };
 
 } // namespace renderengine
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index bc1a4da..20dd996 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -108,8 +108,6 @@
     virtual void genTextures(size_t count, uint32_t* names) = 0;
     virtual void deleteTextures(size_t count, uint32_t const* names) = 0;
     virtual void bindExternalTextureImage(uint32_t texName, const Image& image) = 0;
-    virtual status_t bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer,
-                                               sp<Fence> fence, bool cleanCache) = 0;
     // When binding a native buffer, it must be done before setViewportAndProjection
     // Returns NO_ERROR when binds successfully, NO_MEMORY when there's no memory for allocation.
     virtual status_t bindFrameBuffer(Framebuffer* framebuffer) = 0;
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index 4b86cfe..b4c7c96 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -52,7 +52,6 @@
     MOCK_METHOD2(genTextures, void(size_t, uint32_t*));
     MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*));
     MOCK_METHOD2(bindExternalTextureImage, void(uint32_t, const renderengine::Image&));
-    MOCK_METHOD4(bindExternalTextureBuffer, status_t(uint32_t, sp<GraphicBuffer>, sp<Fence>, bool));
     MOCK_CONST_METHOD0(checkErrors, void());
     MOCK_METHOD4(setViewportAndProjection,
                  void(size_t, size_t, Rect, ui::Transform::orientation_flags));
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 0ee6153..bef25a8 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -194,7 +194,7 @@
 
     void clearLeftRegion();
 
-    void clearRegion();
+    void fillBufferThenClearRegion();
 
     // Dumb hack to get aroud the fact that tear-down for renderengine isn't
     // well defined right now, so we can't create multiple instances
@@ -685,13 +685,14 @@
     invokeDraw(settings, layers, mBuffer);
 }
 
-void RenderEngineTest::clearRegion() {
+void RenderEngineTest::fillBufferThenClearRegion() {
+    fillGreenBuffer<ColorSourceVariant>();
     // Reuse mBuffer
     clearLeftRegion();
     expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT), 0, 0, 0, 255);
     expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH,
                            DEFAULT_DISPLAY_HEIGHT),
-                      0, 0, 0, 0);
+                      0, 255, 0, 255);
 }
 
 TEST_F(RenderEngineTest, drawLayers_noLayersToDraw) {
@@ -854,8 +855,8 @@
     fillBufferWithoutPremultiplyAlpha();
 }
 
-TEST_F(RenderEngineTest, drawLayers_clearRegion) {
-    clearRegion();
+TEST_F(RenderEngineTest, drawLayers_fillBufferThenClearRegion) {
+    fillBufferThenClearRegion();
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index f986329..65308a6 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -57,6 +57,8 @@
                 compositionengine::LayerCreationArgs{this})} {
     ALOGV("Creating Layer %s", args.name.string());
 
+    mTexture.init(renderengine::Texture::TEXTURE_EXTERNAL, mTextureName);
+
     mPremultipliedAlpha = !(args.flags & ISurfaceComposerClient::eNonPremultiplied);
 
     mPotentialCursor = args.flags & ISurfaceComposerClient::eCursorWindow;
@@ -127,11 +129,13 @@
     return inverse(tr);
 }
 
-bool BufferLayer::prepareClientLayer(const RenderArea& renderArea, const Region& clip,
-                                     bool useIdentityTransform, Region& clearRegion,
-                                     renderengine::LayerSettings& layer) {
+/*
+ * onDraw will draw the current layer onto the presentable buffer
+ */
+void BufferLayer::onDraw(const RenderArea& renderArea, const Region& clip,
+                         bool useIdentityTransform) {
     ATRACE_CALL();
-    Layer::prepareClientLayer(renderArea, clip, useIdentityTransform, clearRegion, layer);
+
     if (CC_UNLIKELY(mActiveBuffer == 0)) {
         // the texture has not been created yet, this Layer has
         // in fact never been drawn into. This happens frequently with
@@ -149,27 +153,30 @@
                 finished = true;
                 return;
             }
-            under.orSelf(layer->visibleRegion);
+            under.orSelf(renderArea.getTransform().transform(layer->visibleRegion));
         });
         // if not everything below us is covered, we plug the holes!
         Region holes(clip.subtract(under));
         if (!holes.isEmpty()) {
-            clearRegion.orSelf(holes);
+            clearWithOpenGL(renderArea, 0, 0, 0, 1);
         }
-        return false;
+        return;
     }
+
+    // Bind the current buffer to the GL texture, and wait for it to be
+    // ready for us to draw into.
+    status_t err = bindTextureImage();
+    if (err != NO_ERROR) {
+        ALOGW("onDraw: bindTextureImage failed (err=%d)", err);
+        // Go ahead and draw the buffer anyway; no matter what we do the screen
+        // is probably going to have something visibly wrong.
+    }
+
     bool blackOutLayer = isProtected() || (isSecure() && !renderArea.isSecure());
-    const State& s(getDrawingState());
+
+    auto& engine(mFlinger->getRenderEngine());
+
     if (!blackOutLayer) {
-        layer.source.buffer.buffer = mActiveBuffer;
-        layer.source.buffer.isOpaque = isOpaque(s);
-        layer.source.buffer.fence = mActiveBufferFence;
-        layer.source.buffer.cacheHint = useCachedBufferForClientComposition()
-                ? renderengine::Buffer::CachingHint::USE_CACHE
-                : renderengine::Buffer::CachingHint::NO_CACHE;
-        layer.source.buffer.textureName = mTextureName;
-        layer.source.buffer.usePremultipliedAlpha = getPremultipledAlpha();
-        layer.source.buffer.isY410BT2020 = isHdrY410();
         // TODO: we could be more subtle with isFixedSize()
         const bool useFiltering = needsFiltering() || renderArea.needsFiltering() || isFixedSize();
 
@@ -206,31 +213,17 @@
             memcpy(textureMatrix, texTransform.asArray(), sizeof(textureMatrix));
         }
 
-        const Rect win{computeBounds()};
-        const float bufferWidth = getBufferSize(s).getWidth();
-        const float bufferHeight = getBufferSize(s).getHeight();
+        // Set things up for texturing.
+        mTexture.setDimensions(mActiveBuffer->getWidth(), mActiveBuffer->getHeight());
+        mTexture.setFiltering(useFiltering);
+        mTexture.setMatrix(textureMatrix);
 
-        const float scaleHeight = (float(win.bottom) - float(win.top)) / bufferHeight;
-        const float scaleWidth = (float(win.right) - float(win.left)) / bufferWidth;
-        const float translateY = float(win.top) / bufferHeight;
-        const float translateX = float(win.left) / bufferWidth;
-
-        // Flip y-coordinates because GLConsumer expects OpenGL convention.
-        mat4 tr = mat4::translate(vec4(.5, .5, 0, 1)) * mat4::scale(vec4(1, -1, 1, 1)) *
-                mat4::translate(vec4(-.5, -.5, 0, 1)) *
-                mat4::translate(vec4(translateX, translateY, 0, 1)) *
-                mat4::scale(vec4(scaleWidth, scaleHeight, 1.0, 1.0));
-
-        layer.source.buffer.useTextureFiltering = useFiltering;
-        layer.source.buffer.textureTransform = mat4(static_cast<const float*>(textureMatrix)) * tr;
+        engine.setupLayerTexturing(mTexture);
     } else {
-        // If layer is blacked out, force alpha to 1 so that we draw a black color
-        // layer.
-        layer.source.buffer.buffer = nullptr;
-        layer.alpha = 1.0;
+        engine.setupLayerBlackedOut();
     }
-
-    return true;
+    drawWithOpenGL(renderArea, useIdentityTransform);
+    engine.disableTexturing();
 }
 
 bool BufferLayer::isHdrY410() const {
@@ -613,6 +606,67 @@
             sourceCrop.getWidth() != displayFrame.getWidth();
 }
 
+void BufferLayer::drawWithOpenGL(const RenderArea& renderArea, bool useIdentityTransform) const {
+    ATRACE_CALL();
+    const State& s(getDrawingState());
+
+    computeGeometry(renderArea, getBE().mMesh, useIdentityTransform);
+
+    /*
+     * NOTE: the way we compute the texture coordinates here produces
+     * different results than when we take the HWC path -- in the later case
+     * the "source crop" is rounded to texel boundaries.
+     * This can produce significantly different results when the texture
+     * is scaled by a large amount.
+     *
+     * The GL code below is more logical (imho), and the difference with
+     * HWC is due to a limitation of the HWC API to integers -- a question
+     * is suspend is whether we should ignore this problem or revert to
+     * GL composition when a buffer scaling is applied (maybe with some
+     * minimal value)? Or, we could make GL behave like HWC -- but this feel
+     * like more of a hack.
+     */
+    const Rect bounds{computeBounds()}; // Rounds from FloatRect
+
+    Rect win = bounds;
+    const int bufferWidth = getBufferSize(s).getWidth();
+    const int bufferHeight = getBufferSize(s).getHeight();
+
+    const float left = float(win.left) / float(bufferWidth);
+    const float top = float(win.top) / float(bufferHeight);
+    const float right = float(win.right) / float(bufferWidth);
+    const float bottom = float(win.bottom) / float(bufferHeight);
+
+    // TODO: we probably want to generate the texture coords with the mesh
+    // here we assume that we only have 4 vertices
+    renderengine::Mesh::VertexArray<vec2> texCoords(getBE().mMesh.getTexCoordArray<vec2>());
+    // flip texcoords vertically because BufferLayerConsumer expects them to be in GL convention
+    texCoords[0] = vec2(left, 1.0f - top);
+    texCoords[1] = vec2(left, 1.0f - bottom);
+    texCoords[2] = vec2(right, 1.0f - bottom);
+    texCoords[3] = vec2(right, 1.0f - top);
+
+    const auto roundedCornerState = getRoundedCornerState();
+    const auto cropRect = roundedCornerState.cropRect;
+    setupRoundedCornersCropCoordinates(win, cropRect);
+
+    auto& engine(mFlinger->getRenderEngine());
+    engine.setupLayerBlending(mPremultipliedAlpha, isOpaque(s), false /* disableTexture */,
+                              getColor(), roundedCornerState.radius);
+    engine.setSourceDataSpace(mCurrentDataSpace);
+
+    if (isHdrY410()) {
+        engine.setSourceY410BT2020(true);
+    }
+
+    engine.setupCornerRadiusCropSize(cropRect.getWidth(), cropRect.getHeight());
+
+    engine.drawMesh(getBE().mMesh);
+    engine.disableBlending();
+
+    engine.setSourceY410BT2020(false);
+}
+
 uint64_t BufferLayer::getHeadFrameNumber() const {
     if (hasFrameUpdate()) {
         return getFrameNumber();
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index d0c1b13..8158d6c 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -22,7 +22,6 @@
 
 #include <gui/ISurfaceComposerClient.h>
 #include <gui/LayerState.h>
-#include <renderengine/Image.h>
 #include <renderengine/Mesh.h>
 #include <renderengine/Texture.h>
 #include <system/window.h> // For NATIVE_WINDOW_SCALING_MODE_FREEZE
@@ -78,6 +77,10 @@
     // isFixedSize - true if content has a fixed size
     bool isFixedSize() const override;
 
+    // onDraw - draws the surface.
+    void onDraw(const RenderArea& renderArea, const Region& clip,
+                bool useIdentityTransform) override;
+
     bool isHdrY410() const override;
 
     void setPerFrameData(DisplayId displayId, const ui::Transform& transform, const Rect& viewport,
@@ -165,19 +168,13 @@
 
     bool mRefreshPending{false};
 
-    // Returns true if, when drawing the active buffer during gpu compositon, we
-    // should use a cached buffer or not.
-    virtual bool useCachedBufferForClientComposition() const = 0;
-
-    // prepareClientLayer - constructs a RenderEngine layer for GPU composition.
-    bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
-                            bool useIdentityTransform, Region& clearRegion,
-                            renderengine::LayerSettings& layer);
-
 private:
     // Returns true if this layer requires filtering
     bool needsFiltering() const;
 
+    // drawing
+    void drawWithOpenGL(const RenderArea& renderArea, bool useIdentityTransform) const;
+
     uint64_t getHeadFrameNumber() const;
 
     uint32_t mCurrentScalingMode{NATIVE_WINDOW_SCALING_MODE_FREEZE};
@@ -185,6 +182,9 @@
     // main thread.
     bool mBufferLatched{false}; // TODO: Use mActiveBuffer?
 
+    // The texture used to draw the layer in GLES composition mode
+    mutable renderengine::Texture mTexture;
+
     Rect getBufferSize(const State& s) const override;
 
     std::shared_ptr<compositionengine::Layer> mCompositionLayer;
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index 7ed8184..6826050 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -272,7 +272,6 @@
     // Update the BufferLayerConsumer state.
     mCurrentTexture = slot;
     mCurrentTextureBuffer = nextTextureBuffer;
-    mCurrentTextureBufferStaleForGpu = false;
     mCurrentTextureImageFreed = nullptr;
     mCurrentCrop = item.mCrop;
     mCurrentTransform = item.mTransform;
@@ -295,7 +294,48 @@
 status_t BufferLayerConsumer::bindTextureImageLocked() {
     ATRACE_CALL();
 
-    return mRE.bindExternalTextureBuffer(mTexName, mCurrentTextureBuffer, mCurrentFence, false);
+    mRE.checkErrors();
+
+    // It is possible for the current slot's buffer to be freed before a new one
+    // is bound. In that scenario we still want to bind the image.
+    if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && mCurrentTextureBuffer == nullptr) {
+        BLC_LOGE("bindTextureImage: no currently-bound texture");
+        mRE.bindExternalTextureImage(mTexName, *mRE.createImage());
+        return NO_INIT;
+    }
+
+    renderengine::Image* imageToRender;
+
+    // mCurrentTextureImageFreed is non-null iff mCurrentTexture ==
+    // BufferQueue::INVALID_BUFFER_SLOT, so we can omit that check.
+    if (mCurrentTextureImageFreed) {
+        imageToRender = mCurrentTextureImageFreed.get();
+    } else if (mImages[mCurrentTexture]) {
+        imageToRender = mImages[mCurrentTexture].get();
+    } else {
+        std::unique_ptr<renderengine::Image> image = mRE.createImage();
+        bool success = image->setNativeWindowBuffer(mCurrentTextureBuffer->getNativeBuffer(),
+                                                    mCurrentTextureBuffer->getUsage() &
+                                                            GRALLOC_USAGE_PROTECTED);
+        if (!success) {
+            BLC_LOGE("bindTextureImage: Failed to create image. size=%ux%u st=%u usage=%#" PRIx64
+                     " fmt=%d",
+                     mCurrentTextureBuffer->getWidth(), mCurrentTextureBuffer->getHeight(),
+                     mCurrentTextureBuffer->getStride(), mCurrentTextureBuffer->getUsage(),
+                     mCurrentTextureBuffer->getPixelFormat());
+            BLC_LOGW("bindTextureImage: can't create image on slot=%d", mCurrentTexture);
+            mRE.bindExternalTextureImage(mTexName, *image);
+            return UNKNOWN_ERROR;
+        }
+        imageToRender = image.get();
+        // Cache the image here so that we can reuse it.
+        mImages[mCurrentTexture] = std::move(image);
+    }
+
+    mRE.bindExternalTextureImage(mTexName, *imageToRender);
+
+    // Wait for the new buffer to be ready.
+    return doFenceWaitLocked();
 }
 
 status_t BufferLayerConsumer::syncForReleaseLocked(const sp<Fence>& releaseFence) {
@@ -393,29 +433,16 @@
     return mCurrentApi;
 }
 
-sp<GraphicBuffer> BufferLayerConsumer::getCurrentBuffer(int* outSlot, sp<Fence>* outFence) const {
+sp<GraphicBuffer> BufferLayerConsumer::getCurrentBuffer(int* outSlot) const {
     Mutex::Autolock lock(mMutex);
 
     if (outSlot != nullptr) {
         *outSlot = mCurrentTexture;
     }
 
-    if (outFence != nullptr) {
-        *outFence = mCurrentFence;
-    }
-
     return mCurrentTextureBuffer;
 }
 
-bool BufferLayerConsumer::getAndSetCurrentBufferCacheHint() {
-    Mutex::Autolock lock(mMutex);
-    bool useCache = mCurrentTextureBufferStaleForGpu;
-    // Set the staleness bit here, as this function is only called during a
-    // client composition path.
-    mCurrentTextureBufferStaleForGpu = true;
-    return useCache;
-}
-
 Rect BufferLayerConsumer::getCurrentCrop() const {
     Mutex::Autolock lock(mMutex);
     return (mCurrentScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP)
diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h
index e2ef399..ea46245 100644
--- a/services/surfaceflinger/BufferLayerConsumer.h
+++ b/services/surfaceflinger/BufferLayerConsumer.h
@@ -150,16 +150,10 @@
     // for use with bilinear filtering.
     void setFilteringEnabled(bool enabled);
 
-    // Sets mCurrentTextureBufferStaleForGpu to true to indicate that the
-    // buffer is now "stale" for GPU composition, and returns the old staleness
-    // bit as a caching hint.
-    bool getAndSetCurrentBufferCacheHint();
-
     // getCurrentBuffer returns the buffer associated with the current image.
     // When outSlot is not nullptr, the current buffer slot index is also
-    // returned. Simiarly, when outFence is not nullptr, the current output
-    // fence is returned.
-    sp<GraphicBuffer> getCurrentBuffer(int* outSlot = nullptr, sp<Fence>* outFence = nullptr) const;
+    // returned.
+    sp<GraphicBuffer> getCurrentBuffer(int* outSlot = nullptr) const;
 
     // getCurrentCrop returns the cropping rectangle of the current buffer.
     Rect getCurrentCrop() const;
@@ -261,10 +255,6 @@
     // must track it separately in order to support the getCurrentBuffer method.
     sp<GraphicBuffer> mCurrentTextureBuffer;
 
-    // True if the buffer was used for the previous client composition frame,
-    // and false otherwise.
-    bool mCurrentTextureBufferStaleForGpu;
-
     // mCurrentCrop is the crop rectangle that applies to the current texture.
     // It gets set each time updateTexImage is called.
     Rect mCurrentCrop;
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index edfeb0b..42021d1 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -306,7 +306,7 @@
 
 status_t BufferQueueLayer::updateActiveBuffer() {
     // update the active buffer
-    mActiveBuffer = mConsumer->getCurrentBuffer(&mActiveBufferSlot, &mActiveBufferFence);
+    mActiveBuffer = mConsumer->getCurrentBuffer(&mActiveBufferSlot);
     getBE().compositionInfo.mBuffer = mActiveBuffer;
     getBE().compositionInfo.mBufferSlot = mActiveBufferSlot;
 
@@ -317,10 +317,6 @@
     return NO_ERROR;
 }
 
-bool BufferQueueLayer::useCachedBufferForClientComposition() const {
-    return mConsumer->getAndSetCurrentBufferCacheHint();
-}
-
 status_t BufferQueueLayer::updateFrameNumber(nsecs_t latchTime) {
     mPreviousFrameNumber = mCurrentFrameNumber;
     mCurrentFrameNumber = mConsumer->getFrameNumber();
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 20992a4..d7c3f6a 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -62,9 +62,6 @@
 public:
     bool fenceHasSignaled() const override;
 
-protected:
-    bool useCachedBufferForClientComposition() const override;
-
 private:
     nsecs_t getDesiredPresentTime() override;
     std::shared_ptr<FenceTime> getCurrentFenceTime() const override;
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 2005d20..044662c 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -450,7 +450,46 @@
     const State& s(getDrawingState());
     auto& engine(mFlinger->getRenderEngine());
 
-    return engine.bindExternalTextureBuffer(mTextureName, s.buffer, s.acquireFence, false);
+    engine.checkErrors();
+
+    // TODO(marissaw): once buffers are cached, don't create a new image everytime
+    mTextureImage = engine.createImage();
+
+    bool created =
+            mTextureImage->setNativeWindowBuffer(s.buffer->getNativeBuffer(),
+                                                 s.buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
+    if (!created) {
+        ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d",
+              s.buffer->getWidth(), s.buffer->getHeight(), s.buffer->getStride(),
+              s.buffer->getUsage(), s.buffer->getPixelFormat());
+        engine.bindExternalTextureImage(mTextureName, *engine.createImage());
+        return NO_INIT;
+    }
+
+    engine.bindExternalTextureImage(mTextureName, *mTextureImage);
+
+    // Wait for the new buffer to be ready.
+    if (s.acquireFence->isValid()) {
+        if (SyncFeatures::getInstance().useWaitSync()) {
+            base::unique_fd fenceFd(s.acquireFence->dup());
+            if (fenceFd == -1) {
+                ALOGE("error dup'ing fence fd: %d", errno);
+                return -errno;
+            }
+            if (!engine.waitFence(std::move(fenceFd))) {
+                ALOGE("failed to wait on fence fd");
+                return UNKNOWN_ERROR;
+            }
+        } else {
+            status_t err = s.acquireFence->waitForever("BufferStateLayer::bindTextureImage");
+            if (err != NO_ERROR) {
+                ALOGE("error waiting for fence: %d", err);
+                return err;
+            }
+        }
+    }
+
+    return NO_ERROR;
 }
 
 status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nsecs_t latchTime,
@@ -573,18 +612,12 @@
     }
 
     mActiveBuffer = s.buffer;
-    mActiveBufferFence = s.acquireFence;
     getBE().compositionInfo.mBuffer = mActiveBuffer;
     getBE().compositionInfo.mBufferSlot = 0;
 
     return NO_ERROR;
 }
 
-bool BufferStateLayer::useCachedBufferForClientComposition() const {
-    // TODO: Store a proper staleness bit to support EGLImage caching.
-    return false;
-}
-
 status_t BufferStateLayer::updateFrameNumber(nsecs_t /*latchTime*/) {
     // TODO(marissaw): support frame history events
     mCurrentFrameNumber = mFrameNumber;
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 138bb4c..3f891d3 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -95,9 +95,6 @@
     // -----------------------------------------------------------------------
     bool fenceHasSignaled() const override;
 
-protected:
-    bool useCachedBufferForClientComposition() const override;
-
 private:
     nsecs_t getDesiredPresentTime() override;
     std::shared_ptr<FenceTime> getCurrentFenceTime() const override;
diff --git a/services/surfaceflinger/ColorLayer.cpp b/services/surfaceflinger/ColorLayer.cpp
index d82ff0e..b4e9d17 100644
--- a/services/surfaceflinger/ColorLayer.cpp
+++ b/services/surfaceflinger/ColorLayer.cpp
@@ -44,14 +44,28 @@
 
 ColorLayer::~ColorLayer() = default;
 
-bool ColorLayer::prepareClientLayer(const RenderArea& renderArea, const Region& clip,
-                                    bool useIdentityTransform, Region& clearRegion,
-                                    renderengine::LayerSettings& layer) {
-    Layer::prepareClientLayer(renderArea, clip, useIdentityTransform, clearRegion, layer);
-    half4 color(getColor());
-    half3 solidColor(color.r, color.g, color.b);
-    layer.source.solidColor = solidColor;
-    return true;
+void ColorLayer::onDraw(const RenderArea& renderArea, const Region& /* clip */,
+                        bool useIdentityTransform) {
+    half4 color = getColor();
+    if (color.a > 0) {
+        renderengine::Mesh mesh(renderengine::Mesh::TRIANGLE_FAN, 4, 2);
+        computeGeometry(renderArea, mesh, useIdentityTransform);
+        auto& engine(mFlinger->getRenderEngine());
+
+        Rect win{computeBounds()};
+
+        const auto roundedCornerState = getRoundedCornerState();
+        const auto cropRect = roundedCornerState.cropRect;
+        setupRoundedCornersCropCoordinates(win, cropRect);
+
+        engine.setupLayerBlending(getPremultipledAlpha(), false /* opaque */,
+                                  true /* disableTexture */, color, roundedCornerState.radius);
+
+        engine.setSourceDataSpace(mCurrentDataSpace);
+        engine.setupCornerRadiusCropSize(cropRect.getWidth(), cropRect.getHeight());
+        engine.drawMesh(mesh);
+        engine.disableBlending();
+    }
 }
 
 bool ColorLayer::isVisible() const {
diff --git a/services/surfaceflinger/ColorLayer.h b/services/surfaceflinger/ColorLayer.h
index ed4c2d8..3b98e8c 100644
--- a/services/surfaceflinger/ColorLayer.h
+++ b/services/surfaceflinger/ColorLayer.h
@@ -31,6 +31,8 @@
     std::shared_ptr<compositionengine::Layer> getCompositionLayer() const override;
 
     virtual const char* getTypeId() const { return "ColorLayer"; }
+    virtual void onDraw(const RenderArea& renderArea, const Region& clip,
+                        bool useIdentityTransform);
     bool isVisible() const override;
 
     bool setColor(const half3& color) override;
@@ -42,9 +44,6 @@
 
 protected:
     FloatRect computeCrop(const sp<const DisplayDevice>& /*display*/) const override { return {}; }
-    virtual bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
-                                    bool useIdentityTransform, Region& clearRegion,
-                                    renderengine::LayerSettings& layer);
 
 private:
     std::shared_ptr<compositionengine::Layer> mCompositionLayer;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 84b2423..a7ab472 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -97,9 +97,10 @@
     // TODO(lpique): Make this protected once it is only internally called.
     virtual OutputCompositionState& editState() = 0;
 
-    // Gets the dirty region in layer stack space.
-    // If repaintEverything is true, this will be the full display bounds.
-    virtual Region getDirtyRegion(bool repaintEverything) const = 0;
+    // Gets the physical space dirty region. If repaintEverything is true, this
+    // will be the full display bounds. Internally the dirty region is stored in
+    // logical (aka layer stack) space.
+    virtual Region getPhysicalSpaceDirtyRegion(bool repaintEverything) const = 0;
 
     // Tests whether a given layerStackId belongs in this output.
     // A layer belongs to the output if its layerStackId matches the of the output layerStackId,
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
index ddeb730..dd01b05 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
@@ -75,13 +75,16 @@
     // Allocates a buffer as scratch space for GPU composition
     virtual sp<GraphicBuffer> dequeueBuffer() = 0;
 
-    // Queues the drawn buffer for consumption by HWC. readyFence is the fence
-    // which will fire when the buffer is ready for consumption.
-    virtual void queueBuffer(base::unique_fd&& readyFence) = 0;
+    // Queues the drawn buffer for consumption by HWC
+    virtual void queueBuffer() = 0;
 
     // Called after the HWC calls are made to present the display
     virtual void onPresentDisplayCompleted() = 0;
 
+    // Marks the current buffer has finished, so that it can be presented and
+    // swapped out
+    virtual void finishBuffer() = 0;
+
     // Called to set the viewport and projection state for rendering into this
     // surface
     virtual void setViewportAndProjection() = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 3fd057c..d876c03 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -61,7 +61,7 @@
     const OutputCompositionState& getState() const override;
     OutputCompositionState& editState() override;
 
-    Region getDirtyRegion(bool repaintEverything) const override;
+    Region getPhysicalSpaceDirtyRegion(bool repaintEverything) const override;
     bool belongsInOutput(uint32_t, bool) const override;
 
     compositionengine::OutputLayer* getOutputLayerForLayer(
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
index 2f0fceb..0489310 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
@@ -53,8 +53,9 @@
     status_t beginFrame(bool mustRecompose) override;
     status_t prepareFrame(std::vector<CompositionInfo>& compositionData) override;
     sp<GraphicBuffer> dequeueBuffer() override;
-    void queueBuffer(base::unique_fd&& readyFence) override;
+    void queueBuffer() override;
     void onPresentDisplayCompleted() override;
+    void finishBuffer() override;
     void setViewportAndProjection() override;
     void flip() override;
 
@@ -76,6 +77,9 @@
     const sp<ANativeWindow> mNativeWindow;
     // Current buffer being rendered into
     sp<GraphicBuffer> mGraphicBuffer;
+    // File descriptor indicating that mGraphicBuffer is ready for display, i.e.
+    // that drawing to the buffer is now complete.
+    base::unique_fd mBufferReady;
     const sp<DisplaySurface> mDisplaySurface;
     ui::Size mSize;
     std::uint32_t mPageFlipCount{0};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 2972ad7..e79a44c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -56,7 +56,7 @@
     MOCK_CONST_METHOD0(getState, const OutputCompositionState&());
     MOCK_METHOD0(editState, OutputCompositionState&());
 
-    MOCK_CONST_METHOD1(getDirtyRegion, Region(bool));
+    MOCK_CONST_METHOD1(getPhysicalSpaceDirtyRegion, Region(bool));
     MOCK_CONST_METHOD2(belongsInOutput, bool(uint32_t, bool));
 
     MOCK_CONST_METHOD1(getOutputLayerForLayer,
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
index e9ff330..2269e57 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
@@ -39,8 +39,9 @@
     MOCK_METHOD1(beginFrame, status_t(bool mustRecompose));
     MOCK_METHOD1(prepareFrame, status_t(std::vector<CompositionInfo>& compositionData));
     MOCK_METHOD0(dequeueBuffer, sp<GraphicBuffer>());
-    MOCK_METHOD1(queueBuffer, void(base::unique_fd&&));
+    MOCK_METHOD0(queueBuffer, void());
     MOCK_METHOD0(onPresentDisplayCompleted, void());
+    MOCK_METHOD0(finishBuffer, void());
     MOCK_METHOD0(setViewportAndProjection, void());
     MOCK_METHOD0(flip, void());
     MOCK_CONST_METHOD1(dump, void(std::string& result));
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index f6c1a33..6d5147d 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -170,10 +170,13 @@
     return mState;
 }
 
-Region Output::getDirtyRegion(bool repaintEverything) const {
-    Region dirty(mState.viewport);
-    if (!repaintEverything) {
-        dirty.andSelf(mState.dirtyRegion);
+Region Output::getPhysicalSpaceDirtyRegion(bool repaintEverything) const {
+    Region dirty;
+    if (repaintEverything) {
+        dirty.set(mState.bounds);
+    } else {
+        dirty = mState.transform.transform(mState.dirtyRegion);
+        dirty.andSelf(mState.bounds);
     }
     return dirty;
 }
diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
index 3f841d2..d546fc8 100644
--- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
@@ -156,7 +156,7 @@
     return mGraphicBuffer;
 }
 
-void RenderSurface::queueBuffer(base::unique_fd&& readyFence) {
+void RenderSurface::queueBuffer() {
     auto& hwc = mCompositionEngine.getHwComposer();
     const auto id = mDisplay.getId();
 
@@ -178,9 +178,9 @@
         if (mGraphicBuffer == nullptr) {
             ALOGE("No buffer is ready for display [%s]", mDisplay.getName().c_str());
         } else {
-            status_t result =
-                    mNativeWindow->queueBuffer(mNativeWindow.get(),
-                                               mGraphicBuffer->getNativeBuffer(), dup(readyFence));
+            status_t result = mNativeWindow->queueBuffer(mNativeWindow.get(),
+                                                         mGraphicBuffer->getNativeBuffer(),
+                                                         dup(mBufferReady));
             if (result != NO_ERROR) {
                 ALOGE("Error when queueing buffer for display [%s]: %d", mDisplay.getName().c_str(),
                       result);
@@ -190,10 +190,12 @@
                     LOG_ALWAYS_FATAL("ANativeWindow::queueBuffer failed with error: %d", result);
                 } else {
                     mNativeWindow->cancelBuffer(mNativeWindow.get(),
-                                                mGraphicBuffer->getNativeBuffer(), dup(readyFence));
+                                                mGraphicBuffer->getNativeBuffer(),
+                                                dup(mBufferReady));
                 }
             }
 
+            mBufferReady.reset();
             mGraphicBuffer = nullptr;
         }
     }
@@ -215,6 +217,14 @@
                                           ui::Transform::ROT_0);
 }
 
+void RenderSurface::finishBuffer() {
+    auto& renderEngine = mCompositionEngine.getRenderEngine();
+    mBufferReady = renderEngine.flush();
+    if (mBufferReady.get() < 0) {
+        renderEngine.finish();
+    }
+}
+
 void RenderSurface::flip() {
     mPageFlipCount++;
 }
@@ -253,5 +263,9 @@
     return mGraphicBuffer;
 }
 
+base::unique_fd& RenderSurface::mutableBufferReadyForTest() {
+    return mBufferReady;
+}
+
 } // namespace impl
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 314d98b..cc1211b 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -215,32 +215,52 @@
 }
 
 /* ------------------------------------------------------------------------
- * Output::getDirtyRegion()
+ * Output::getPhysicalSpaceDirtyRegion()
  */
 
-TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingTrue) {
-    const Rect viewport{100, 200};
-    mOutput.editState().viewport = viewport;
+TEST_F(OutputTest, getPhysicalSpaceDirtyRegionWithRepaintEverythingTrue) {
+    const Rect displaySize{100, 200};
+    mOutput.editState().bounds = displaySize;
     mOutput.editState().dirtyRegion.set(50, 300);
 
     {
-        Region result = mOutput.getDirtyRegion(true);
+        Region result = mOutput.getPhysicalSpaceDirtyRegion(true);
 
-        EXPECT_THAT(result, RegionEq(Region(viewport)));
+        EXPECT_THAT(result, RegionEq(Region(displaySize)));
+    }
+
+    // For repaint everything == true, the returned value does not depend on the display
+    // rotation.
+    mOutput.editState().transform.set(ui::Transform::ROT_90, 0, 0);
+
+    {
+        Region result = mOutput.getPhysicalSpaceDirtyRegion(true);
+
+        EXPECT_THAT(result, RegionEq(Region(displaySize)));
     }
 }
 
-TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingFalse) {
-    const Rect viewport{100, 200};
-    mOutput.editState().viewport = viewport;
+TEST_F(OutputTest, getPhysicalSpaceDirtyRegionWithRepaintEverythingFalse) {
+    const Rect displaySize{100, 200};
+    mOutput.editState().bounds = displaySize;
     mOutput.editState().dirtyRegion.set(50, 300);
 
     {
-        Region result = mOutput.getDirtyRegion(false);
+        Region result = mOutput.getPhysicalSpaceDirtyRegion(false);
 
         // The dirtyRegion should be clipped to the display bounds.
         EXPECT_THAT(result, RegionEq(Region(Rect(50, 200))));
     }
+
+    mOutput.editState().transform.set(ui::Transform::ROT_90, displaySize.getWidth(),
+                                      displaySize.getHeight());
+
+    {
+        Region result = mOutput.getPhysicalSpaceDirtyRegion(false);
+
+        // The dirtyRegion should be rotated and clipped to the display bounds.
+        EXPECT_THAT(result, RegionEq(Region(Rect(100, 50))));
+    }
 }
 
 /* ------------------------------------------------------------------------
diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
index c56d92a..13cc663 100644
--- a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
@@ -373,7 +373,7 @@
             .WillOnce(Return(false));
     EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
 
-    mSurface.queueBuffer(base::unique_fd());
+    mSurface.queueBuffer();
 
     EXPECT_EQ(buffer.get(), mSurface.mutableGraphicBufferForTest().get());
 }
@@ -387,7 +387,7 @@
             .WillOnce(Return(NO_ERROR));
     EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
 
-    mSurface.queueBuffer(base::unique_fd());
+    mSurface.queueBuffer();
 
     EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
 }
@@ -402,7 +402,7 @@
             .WillOnce(Return(NO_ERROR));
     EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
 
-    mSurface.queueBuffer(base::unique_fd());
+    mSurface.queueBuffer();
 
     EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
 }
@@ -419,7 +419,7 @@
             .WillOnce(Return(NO_ERROR));
     EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
 
-    mSurface.queueBuffer(base::unique_fd());
+    mSurface.queueBuffer();
 
     EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
 }
@@ -436,7 +436,7 @@
             .WillOnce(Return(NO_ERROR));
     EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
 
-    mSurface.queueBuffer(base::unique_fd());
+    mSurface.queueBuffer();
 
     EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
 }
@@ -452,6 +452,29 @@
 }
 
 /* ------------------------------------------------------------------------
+ * RenderSurface::finishBuffer()
+ */
+
+TEST_F(RenderSurfaceTest, finishBufferJustFlushesRenderEngine) {
+    int fd = dup(1);
+
+    EXPECT_CALL(mRenderEngine, flush()).WillOnce(Return(ByMove(base::unique_fd(fd))));
+
+    mSurface.finishBuffer();
+
+    EXPECT_EQ(fd, mSurface.mutableBufferReadyForTest().release());
+}
+
+TEST_F(RenderSurfaceTest, finishBufferFlushesAndFinishesRenderEngine) {
+    EXPECT_CALL(mRenderEngine, flush()).WillOnce(Return(ByMove(base::unique_fd(-2))));
+    EXPECT_CALL(mRenderEngine, finish()).Times(1);
+
+    mSurface.finishBuffer();
+
+    EXPECT_EQ(-2, mSurface.mutableBufferReadyForTest().release());
+}
+
+/* ------------------------------------------------------------------------
  * RenderSurface::setViewportAndProjection()
  */
 
diff --git a/services/surfaceflinger/ContainerLayer.cpp b/services/surfaceflinger/ContainerLayer.cpp
index 9530398..ca49f6c 100644
--- a/services/surfaceflinger/ContainerLayer.cpp
+++ b/services/surfaceflinger/ContainerLayer.cpp
@@ -26,10 +26,7 @@
 
 ContainerLayer::~ContainerLayer() = default;
 
-bool ContainerLayer::prepareClientLayer(const RenderArea&, const Region&, bool, Region&,
-                                        renderengine::LayerSettings&) {
-    return false;
-}
+void ContainerLayer::onDraw(const RenderArea&, const Region& /* clip */, bool) {}
 
 bool ContainerLayer::isVisible() const {
     return !isHiddenByPolicy();
diff --git a/services/surfaceflinger/ContainerLayer.h b/services/surfaceflinger/ContainerLayer.h
index 409cf22..413844b 100644
--- a/services/surfaceflinger/ContainerLayer.h
+++ b/services/surfaceflinger/ContainerLayer.h
@@ -29,6 +29,8 @@
     ~ContainerLayer() override;
 
     const char* getTypeId() const override { return "ContainerLayer"; }
+    void onDraw(const RenderArea& renderArea, const Region& clip,
+                bool useIdentityTransform) override;
     bool isVisible() const override;
 
     void setPerFrameData(DisplayId displayId, const ui::Transform& transform, const Rect& viewport,
@@ -37,11 +39,6 @@
     bool isCreatedFromMainThread() const override { return true; }
 
     bool onPreComposition(nsecs_t /*refreshStartTime*/) override { return false; }
-
-protected:
-    bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
-                            bool useIdentityTransform, Region& clearRegion,
-                            renderengine::LayerSettings& layer);
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 185393e..2de169d 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -639,7 +639,7 @@
          * Here we cancel out the orientation component of the WM transform.
          * The scaling and translate components are already included in our bounds
          * computation so it's enough to just omit it in the composition.
-         * See comment in BufferLayer::prepareClientLayer with ref to b/36727915 for why.
+         * See comment in onDraw with ref to b/36727915 for why.
          */
         transform = ui::Transform(invTransform) * tr * bufferOrientation;
     }
@@ -707,51 +707,12 @@
 // drawing...
 // ---------------------------------------------------------------------------
 
-bool Layer::prepareClientLayer(const RenderArea& renderArea, const Region& clip,
-                               Region& clearRegion, renderengine::LayerSettings& layer) {
-    return prepareClientLayer(renderArea, clip, false, clearRegion, layer);
+void Layer::draw(const RenderArea& renderArea, const Region& clip) {
+    onDraw(renderArea, clip, false);
 }
 
-bool Layer::prepareClientLayer(const RenderArea& renderArea, bool useIdentityTransform,
-                               Region& clearRegion, renderengine::LayerSettings& layer) {
-    return prepareClientLayer(renderArea, Region(renderArea.getBounds()), useIdentityTransform,
-                              clearRegion, layer);
-}
-
-bool Layer::prepareClientLayer(const RenderArea& /*renderArea*/, const Region& /*clip*/,
-                               bool useIdentityTransform, Region& /*clearRegion*/,
-                               renderengine::LayerSettings& layer) {
-    FloatRect bounds = computeBounds();
-    half alpha = getAlpha();
-    layer.geometry.boundaries = bounds;
-    if (useIdentityTransform) {
-        layer.geometry.positionTransform = mat4();
-    } else {
-        const ui::Transform transform = getTransform();
-        mat4 m;
-        m[0][0] = transform[0][0];
-        m[0][1] = transform[0][1];
-        m[0][3] = transform[0][2];
-        m[1][0] = transform[1][0];
-        m[1][1] = transform[1][1];
-        m[1][3] = transform[1][2];
-        m[3][0] = transform[2][0];
-        m[3][1] = transform[2][1];
-        m[3][3] = transform[2][2];
-        layer.geometry.positionTransform = m;
-    }
-
-    if (hasColorTransform()) {
-        layer.colorTransform = getColorTransform();
-    }
-
-    const auto roundedCornerState = getRoundedCornerState();
-    layer.geometry.roundedCornersRadius = roundedCornerState.radius;
-    layer.geometry.roundedCornersCrop = roundedCornerState.cropRect;
-
-    layer.alpha = alpha;
-    layer.sourceDataspace = mCurrentDataSpace;
-    return true;
+void Layer::draw(const RenderArea& renderArea, bool useIdentityTransform) {
+    onDraw(renderArea, Region(renderArea.getBounds()), useIdentityTransform);
 }
 
 void Layer::clearWithOpenGL(const RenderArea& renderArea, float red, float green, float blue,
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 4cead87..f099df6 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -421,9 +421,11 @@
     virtual Rect getCrop(const Layer::State& s) const { return s.crop_legacy; }
 
 protected:
-    virtual bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
-                                    bool useIdentityTransform, Region& clearRegion,
-                                    renderengine::LayerSettings& layer) = 0;
+    /*
+     * onDraw - draws the surface.
+     */
+    virtual void onDraw(const RenderArea& renderArea, const Region& clip,
+                        bool useIdentityTransform) = 0;
 
 public:
     virtual void setDefaultBufferSize(uint32_t /*w*/, uint32_t /*h*/) {}
@@ -473,14 +475,11 @@
     virtual void releasePendingBuffer(nsecs_t /*dequeueReadyTime*/) { }
 
     /*
-     * prepareClientLayer - populates a renderengine::LayerSettings to passed to
-     * RenderEngine::drawLayers. Returns true if the layer can be used, and
-     * false otherwise.
+     * draw - performs some global clipping optimizations
+     * and calls onDraw().
      */
-    bool prepareClientLayer(const RenderArea& renderArea, const Region& clip, Region& clearRegion,
-                            renderengine::LayerSettings& layer);
-    bool prepareClientLayer(const RenderArea& renderArea, bool useIdentityTransform,
-                            Region& clearRegion, renderengine::LayerSettings& layer);
+    void draw(const RenderArea& renderArea, const Region& clip);
+    void draw(const RenderArea& renderArea, bool useIdentityTransform);
 
     /*
      * doTransaction - process the transaction. This is a good place to figure
@@ -811,13 +810,7 @@
     FenceTimeline mReleaseTimeline;
 
     // main thread
-    // Active buffer fields
     sp<GraphicBuffer> mActiveBuffer;
-    sp<Fence> mActiveBufferFence;
-    // False if the buffer and its contents have been previously used for GPU
-    // composition, true otherwise.
-    bool mIsActiveBufferUpdatedForGpu = true;
-
     ui::Dataspace mCurrentDataSpace = ui::Dataspace::UNKNOWN;
     Rect mCurrentCrop;
     uint32_t mCurrentTransform{0};
@@ -873,6 +866,7 @@
                                        const LayerVector::Visitor& visitor);
     LayerVector makeChildrenTraversalList(LayerVector::StateSet stateSet,
                                           const std::vector<Layer*>& layersInTree);
+
     /**
      * Retuns the child bounds in layer space cropped to its bounds as well all its parent bounds.
      * The cropped bounds must be transformed back from parent layer space to child layer space by
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index cf2b80c..96496f0 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-//#define LOG_NDEBUG 0
+// #define LOG_NDEBUG 0
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include <sys/types.h>
@@ -1873,13 +1873,16 @@
 
     if (displayState.isEnabled) {
         // transform the dirty region into this screen's coordinate space
-        const Region dirtyRegion = display->getDirtyRegion(repaintEverything);
+        const Region dirtyRegion = display->getPhysicalSpaceDirtyRegion(repaintEverything);
         if (!dirtyRegion.isEmpty()) {
-            base::unique_fd readyFence;
             // redraw the whole screen
-            doComposeSurfaces(displayDevice, dirtyRegion, &readyFence);
+            doComposeSurfaces(displayDevice);
 
-            display->getRenderSurface()->queueBuffer(std::move(readyFence));
+            // and draw the dirty region
+            auto& engine(getRenderEngine());
+            engine.fillRegionWithColor(dirtyRegion, 1, 0, 1, 1);
+
+            display->getRenderSurface()->queueBuffer();
         }
     }
 
@@ -2414,7 +2417,7 @@
     auto display = displayDevice->getCompositionDisplay();
     const auto& displayState = display->getState();
 
-    bool dirty = !display->getDirtyRegion(false).isEmpty();
+    bool dirty = !display->getPhysicalSpaceDirtyRegion(false).isEmpty();
     bool empty = displayDevice->getVisibleLayersSortedByZ().size() == 0;
     bool wasEmpty = !displayState.lastCompositionHadVisibleLayers;
 
@@ -2465,7 +2468,7 @@
 
     if (displayState.isEnabled) {
         // transform the dirty region into this screen's coordinate space
-        const Region dirtyRegion = display->getDirtyRegion(repaintEverything);
+        const Region dirtyRegion = display->getPhysicalSpaceDirtyRegion(repaintEverything);
 
         // repaint the framebuffer (if needed)
         doDisplayComposition(displayDevice, dirtyRegion);
@@ -3350,15 +3353,13 @@
     }
 
     ALOGV("doDisplayComposition");
-    base::unique_fd readyFence;
-    if (!doComposeSurfaces(displayDevice, Region::INVALID_REGION, &readyFence)) return;
+    if (!doComposeSurfaces(displayDevice)) return;
 
     // swap buffers (presentation)
-    display->getRenderSurface()->queueBuffer(std::move(readyFence));
+    display->getRenderSurface()->queueBuffer();
 }
 
-bool SurfaceFlinger::doComposeSurfaces(const sp<DisplayDevice>& displayDevice,
-                                       const Region& debugRegion, base::unique_fd* readyFence) {
+bool SurfaceFlinger::doComposeSurfaces(const sp<DisplayDevice>& displayDevice) {
     ALOGV("doComposeSurfaces");
 
     auto display = displayDevice->getCompositionDisplay();
@@ -3373,14 +3374,13 @@
     mat4 colorMatrix;
     bool applyColorMatrix = false;
 
-    renderengine::DisplaySettings clientCompositionDisplay;
-    std::vector<renderengine::LayerSettings> clientCompositionLayers;
-    sp<GraphicBuffer> buf;
+    // Framebuffer will live in this scope for GPU composition.
+    std::unique_ptr<renderengine::BindNativeBufferAsFramebuffer> fbo;
 
     if (hasClientComposition) {
         ALOGV("hasClientComposition");
 
-        buf = display->getRenderSurface()->dequeueBuffer();
+        sp<GraphicBuffer> buf = display->getRenderSurface()->dequeueBuffer();
 
         if (buf == nullptr) {
             ALOGW("Dequeuing buffer for display [%s] failed, bailing out of "
@@ -3389,30 +3389,24 @@
             return false;
         }
 
-        clientCompositionDisplay.physicalDisplay = displayState.scissor;
-        clientCompositionDisplay.clip = displayState.scissor;
-        const ui::Transform& displayTransform = displayState.transform;
-        mat4 m;
-        m[0][0] = displayTransform[0][0];
-        m[0][1] = displayTransform[0][1];
-        m[0][3] = displayTransform[0][2];
-        m[1][0] = displayTransform[1][0];
-        m[1][1] = displayTransform[1][1];
-        m[1][3] = displayTransform[1][2];
-        m[3][0] = displayTransform[2][0];
-        m[3][1] = displayTransform[2][1];
-        m[3][3] = displayTransform[2][2];
+        // Bind the framebuffer in this scope.
+        fbo = std::make_unique<renderengine::BindNativeBufferAsFramebuffer>(getRenderEngine(),
+                                                                            buf->getNativeBuffer());
 
-        clientCompositionDisplay.globalTransform = m;
+        if (fbo->getStatus() != NO_ERROR) {
+            ALOGW("Binding buffer for display [%s] failed with status: %d",
+                  displayDevice->getDisplayName().c_str(), fbo->getStatus());
+            return false;
+        }
 
         const auto* profile = display->getDisplayColorProfile();
         Dataspace outputDataspace = Dataspace::UNKNOWN;
         if (profile->hasWideColorGamut()) {
             outputDataspace = displayState.dataspace;
         }
-        clientCompositionDisplay.outputDataspace = outputDataspace;
-        clientCompositionDisplay.maxLuminance =
-                profile->getHdrCapabilities().getDesiredMaxLuminance();
+        getRenderEngine().setOutputDataSpace(outputDataspace);
+        getRenderEngine().setDisplayMaxLuminance(
+                profile->getHdrCapabilities().getDesiredMaxLuminance());
 
         const bool hasDeviceComposition = getHwComposer().hasDeviceComposition(displayId);
         const bool skipClientColorTransform =
@@ -3423,7 +3417,44 @@
         // Compute the global color transform matrix.
         applyColorMatrix = !hasDeviceComposition && !skipClientColorTransform;
         if (applyColorMatrix) {
-            clientCompositionDisplay.colorTransform = colorMatrix;
+            colorMatrix = mDrawingState.colorMatrix;
+        }
+
+        display->getRenderSurface()->setViewportAndProjection();
+
+        // Never touch the framebuffer if we don't have any framebuffer layers
+        if (hasDeviceComposition) {
+            // when using overlays, we assume a fully transparent framebuffer
+            // NOTE: we could reduce how much we need to clear, for instance
+            // remove where there are opaque FB layers. however, on some
+            // GPUs doing a "clean slate" clear might be more efficient.
+            // We'll revisit later if needed.
+            getRenderEngine().clearWithColor(0, 0, 0, 0);
+        } else {
+            // we start with the whole screen area and remove the scissor part
+            // we're left with the letterbox region
+            // (common case is that letterbox ends-up being empty)
+            const Region letterbox = bounds.subtract(displayState.scissor);
+
+            // compute the area to clear
+            const Region region = displayState.undefinedRegion.merge(letterbox);
+
+            // screen is already cleared here
+            if (!region.isEmpty()) {
+                // can happen with SurfaceView
+                drawWormhole(region);
+            }
+        }
+
+        const Rect& bounds = displayState.bounds;
+        const Rect& scissor = displayState.scissor;
+        if (scissor != bounds) {
+            // scissor doesn't match the screen's dimensions, so we
+            // need to clear everything outside of it and enable
+            // the GL scissor so we don't draw anything where we shouldn't
+
+            // enable scissor for this frame
+            getRenderEngine().setScissor(scissor);
         }
     }
 
@@ -3432,11 +3463,10 @@
      */
 
     ALOGV("Rendering client layers");
+    const ui::Transform& displayTransform = displayState.transform;
     bool firstLayer = true;
-    Region clearRegion = Region::INVALID_REGION;
     for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
-        const Region viewportRegion(displayState.viewport);
-        const Region clip(viewportRegion.intersect(layer->visibleRegion));
+        const Region clip(bounds.intersect(displayTransform.transform(layer->visibleRegion)));
         ALOGV("Layer: %s", layer->getName().string());
         ALOGV("  Composition type: %s", to_string(layer->getCompositionType(displayId)).c_str());
         if (!clip.isEmpty()) {
@@ -3452,28 +3482,22 @@
                         layer->getRoundedCornerState().radius == 0.0f && hasClientComposition) {
                         // never clear the very first layer since we're
                         // guaranteed the FB is already cleared
-                        renderengine::LayerSettings layerSettings;
-                        Region dummyRegion;
-                        bool prepared = layer->prepareClientLayer(renderArea, clip, dummyRegion,
-                                                                  layerSettings);
-
-                        if (prepared) {
-                            layerSettings.source.buffer.buffer = nullptr;
-                            layerSettings.source.solidColor = half3(0.0, 0.0, 0.0);
-                            layerSettings.alpha = half(0.0);
-                            layerSettings.disableBlending = true;
-                            clientCompositionLayers.push_back(layerSettings);
-                        }
+                        layer->clearWithOpenGL(renderArea);
                     }
                     break;
                 }
                 case HWC2::Composition::Client: {
-                    renderengine::LayerSettings layerSettings;
-                    bool prepared =
-                            layer->prepareClientLayer(renderArea, clip, clearRegion, layerSettings);
-                    if (prepared) {
-                        clientCompositionLayers.push_back(layerSettings);
+                    if (layer->hasColorTransform()) {
+                        mat4 tmpMatrix;
+                        if (applyColorMatrix) {
+                            tmpMatrix = mDrawingState.colorMatrix;
+                        }
+                        tmpMatrix *= layer->getColorTransform();
+                        getRenderEngine().setColorTransform(tmpMatrix);
+                    } else {
+                        getRenderEngine().setColorTransform(colorMatrix);
                     }
+                    layer->draw(renderArea, clip);
                     break;
                 }
                 default:
@@ -3485,23 +3509,14 @@
         firstLayer = false;
     }
 
+    // Perform some cleanup steps if we used client composition.
     if (hasClientComposition) {
-        clientCompositionDisplay.clearRegion = clearRegion;
-        if (!debugRegion.isEmpty()) {
-            Region::const_iterator it = debugRegion.begin();
-            Region::const_iterator end = debugRegion.end();
-            while (it != end) {
-                const Rect& rect = *it++;
-                renderengine::LayerSettings layerSettings;
-                layerSettings.source.buffer.buffer = nullptr;
-                layerSettings.source.solidColor = half3(1.0, 0.0, 1.0);
-                layerSettings.geometry.boundaries = rect.toFloatRect();
-                layerSettings.alpha = half(1.0);
-                clientCompositionLayers.push_back(layerSettings);
-            }
-        }
-        getRenderEngine().drawLayers(clientCompositionDisplay, clientCompositionLayers,
-                                     buf->getNativeBuffer(), readyFence);
+        getRenderEngine().setColorTransform(mat4());
+        getRenderEngine().disableScissor();
+        display->getRenderSurface()->finishBuffer();
+        // Clear out error flags here so that we don't wait until next
+        // composition to log.
+        getRenderEngine().checkErrors();
     }
     return true;
 }
@@ -5631,109 +5646,35 @@
 
 void SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea,
                                             TraverseLayersFunction traverseLayers,
-                                            ANativeWindowBuffer* buffer, bool useIdentityTransform,
-                                            int* outSyncFd) {
+                                            bool useIdentityTransform) {
     ATRACE_CALL();
 
+    auto& engine(getRenderEngine());
+
     const auto reqWidth = renderArea.getReqWidth();
     const auto reqHeight = renderArea.getReqHeight();
     const auto sourceCrop = renderArea.getSourceCrop();
     const auto rotation = renderArea.getRotationFlags();
 
-    renderengine::DisplaySettings clientCompositionDisplay;
-    std::vector<renderengine::LayerSettings> clientCompositionLayers;
+    engine.setOutputDataSpace(renderArea.getReqDataSpace());
+    engine.setDisplayMaxLuminance(DisplayDevice::sDefaultMaxLumiance);
 
-    // assume that bounds are never offset, and that they are the same as the
-    // buffer bounds.
-    clientCompositionDisplay.physicalDisplay = Rect(reqWidth, reqHeight);
-    ui::Transform transform = renderArea.getTransform();
-    mat4 m;
-    m[0][0] = transform[0][0];
-    m[0][1] = transform[0][1];
-    m[0][3] = transform[0][2];
-    m[1][0] = transform[1][0];
-    m[1][1] = transform[1][1];
-    m[1][3] = transform[1][2];
-    m[3][0] = transform[2][0];
-    m[3][1] = transform[2][1];
-    m[3][3] = transform[2][2];
+    // make sure to clear all GL error flags
+    engine.checkErrors();
 
-    clientCompositionDisplay.globalTransform = m;
-    mat4 rotMatrix;
-    // Displacement for repositioning the clipping rectangle after rotating it
-    // with the rotation hint.
-    int displacementX = 0;
-    int displacementY = 0;
-    float rot90InRadians = 2.0f * static_cast<float>(M_PI) / 4.0f;
-    switch (rotation) {
-        case ui::Transform::ROT_90:
-            rotMatrix = mat4::rotate(rot90InRadians, vec3(0, 0, 1));
-            displacementX = reqWidth;
-            break;
-        case ui::Transform::ROT_180:
-            rotMatrix = mat4::rotate(rot90InRadians * 2.0f, vec3(0, 0, 1));
-            displacementX = reqWidth;
-            displacementY = reqHeight;
-            break;
-        case ui::Transform::ROT_270:
-            rotMatrix = mat4::rotate(rot90InRadians * 3.0f, vec3(0, 0, 1));
-            displacementY = reqHeight;
-            break;
-        default:
-            break;
-    }
-    // We need to transform the clipping window into the right spot.
-    // First, rotate the clipping rectangle by the rotation hint to get the
-    // right orientation
-    const vec4 clipTL = vec4(sourceCrop.left, sourceCrop.top, 0, 1);
-    const vec4 clipBR = vec4(sourceCrop.right, sourceCrop.bottom, 0, 1);
-    const vec4 rotClipTL = rotMatrix * clipTL;
-    const vec4 rotClipBR = rotMatrix * clipBR;
-    const int newClipLeft = std::min(rotClipTL[0], rotClipBR[0]);
-    const int newClipTop = std::min(rotClipTL[1], rotClipBR[1]);
-    const int newClipRight = std::max(rotClipTL[0], rotClipBR[0]);
-    const int newClipBottom = std::max(rotClipTL[1], rotClipBR[1]);
-
-    // Now reposition the clipping rectangle with the displacement vector
-    // computed above.
-    const mat4 displacementMat = mat4::translate(vec4(displacementX, displacementY, 0, 1));
-
-    clientCompositionDisplay.clip =
-            Rect(newClipLeft + displacementX, newClipTop + displacementY,
-                 newClipRight + displacementX, newClipBottom + displacementY);
-
-    // We need to perform the same transformation in layer space, so propagate
-    // it to the global transform.
-    mat4 clipTransform = displacementMat * rotMatrix;
-    clientCompositionDisplay.globalTransform *= clipTransform;
-    clientCompositionDisplay.outputDataspace = renderArea.getReqDataSpace();
-    clientCompositionDisplay.maxLuminance = DisplayDevice::sDefaultMaxLumiance;
+    // set-up our viewport
+    engine.setViewportAndProjection(reqWidth, reqHeight, sourceCrop, rotation);
+    engine.disableTexturing();
 
     const float alpha = RenderArea::getCaptureFillValue(renderArea.getCaptureFill());
+    // redraw the screen entirely...
+    engine.clearWithColor(0, 0, 0, alpha);
 
-    renderengine::LayerSettings fillLayer;
-    fillLayer.source.buffer.buffer = nullptr;
-    fillLayer.source.solidColor = half3(0.0, 0.0, 0.0);
-    fillLayer.geometry.boundaries = FloatRect(0.0, 0.0, 1.0, 1.0);
-    fillLayer.alpha = half(alpha);
-    clientCompositionLayers.push_back(fillLayer);
-
-    Region clearRegion = Region::INVALID_REGION;
     traverseLayers([&](Layer* layer) {
-        renderengine::LayerSettings layerSettings;
-        bool prepared = layer->prepareClientLayer(renderArea, useIdentityTransform, clearRegion,
-                                                  layerSettings);
-        if (prepared) {
-            clientCompositionLayers.push_back(layerSettings);
-        }
+        engine.setColorTransform(layer->getColorTransform());
+        layer->draw(renderArea, useIdentityTransform);
+        engine.setColorTransform(mat4());
     });
-
-    clientCompositionDisplay.clearRegion = clearRegion;
-    base::unique_fd drawFence;
-    getRenderEngine().drawLayers(clientCompositionDisplay, clientCompositionLayers, buffer,
-                                 &drawFence);
-
-    *outSyncFd = drawFence.release();
 }
 
 status_t SurfaceFlinger::captureScreenImplLocked(const RenderArea& renderArea,
@@ -5757,7 +5698,28 @@
         ALOGW("FB is protected: PERMISSION_DENIED");
         return PERMISSION_DENIED;
     }
-    renderScreenImplLocked(renderArea, traverseLayers, buffer, useIdentityTransform, outSyncFd);
+    auto& engine(getRenderEngine());
+
+    // this binds the given EGLImage as a framebuffer for the
+    // duration of this scope.
+    renderengine::BindNativeBufferAsFramebuffer bufferBond(engine, buffer);
+    if (bufferBond.getStatus() != NO_ERROR) {
+        ALOGE("got ANWB binding error while taking screenshot");
+        return INVALID_OPERATION;
+    }
+
+    // this will in fact render into our dequeued buffer
+    // via an FBO, which means we didn't have to create
+    // an EGLSurface and therefore we're not
+    // dependent on the context's EGLConfig.
+    renderScreenImplLocked(renderArea, traverseLayers, useIdentityTransform);
+
+    base::unique_fd syncFd = engine.flush();
+    if (syncFd < 0) {
+        engine.finish();
+    }
+    *outSyncFd = syncFd.release();
+
     return NO_ERROR;
 }
 
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 9aaf6de..d108ad1 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -600,8 +600,7 @@
     void startBootAnim();
 
     void renderScreenImplLocked(const RenderArea& renderArea, TraverseLayersFunction traverseLayers,
-                                ANativeWindowBuffer* buffer, bool useIdentityTransform,
-                                int* outSyncFd);
+                                bool useIdentityTransform);
     status_t captureScreenCommon(RenderArea& renderArea, TraverseLayersFunction traverseLayers,
                                  sp<GraphicBuffer>* outBuffer, const ui::PixelFormat reqPixelFormat,
                                  bool useIdentityTransform);
@@ -740,11 +739,8 @@
     void logLayerStats();
     void doDisplayComposition(const sp<DisplayDevice>& display, const Region& dirtyRegion);
 
-    // This fails if using GL and the surface has been destroyed. readyFence
-    // will be populated if using GL and native fence sync is supported, to
-    // signal when drawing has completed.
-    bool doComposeSurfaces(const sp<DisplayDevice>& display, const Region& debugRegionm,
-                           base::unique_fd* readyFence);
+    // This fails if using GL and the surface has been destroyed.
+    bool doComposeSurfaces(const sp<DisplayDevice>& display);
 
     void postFramebuffer(const sp<DisplayDevice>& display);
     void postFrame();
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 1c4a661..ab9aadc 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -45,10 +45,8 @@
 
 using testing::_;
 using testing::AtLeast;
-using testing::Between;
 using testing::ByMove;
 using testing::DoAll;
-using testing::Field;
 using testing::Invoke;
 using testing::IsNull;
 using testing::Mock;
@@ -160,6 +158,7 @@
     renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
     mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
     mock::DispSync* mPrimaryDispSync = new mock::DispSync();
+    renderengine::mock::Image* mReImage = new renderengine::mock::Image();
     renderengine::mock::Framebuffer* mReFrameBuffer = new renderengine::mock::Framebuffer();
 
     sp<Fence> mClientTargetAcquireFence = Fence::NO_FENCE;
@@ -271,10 +270,11 @@
         EXPECT_CALL(*test->mComposer, getReleaseFences(HWC_DISPLAY, _, _)).Times(1);
 
         EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true));
-        // TODO: remove once we verify that we can just grab the fence from the
-        // FramebufferSurface.
+        EXPECT_CALL(*test->mRenderEngine, checkErrors()).WillRepeatedly(Return());
+        EXPECT_CALL(*test->mRenderEngine, isCurrent()).WillRepeatedly(Return(true));
+
         EXPECT_CALL(*test->mRenderEngine, flush()).WillRepeatedly(Invoke([]() {
-            return base::unique_fd();
+            return base::unique_fd(0);
         }));
 
         EXPECT_CALL(*test->mDisplaySurface, onFrameCommitted()).Times(1);
@@ -286,18 +286,36 @@
 
     template <typename Case>
     static void setupCommonScreensCaptureCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mRenderEngine, drawLayers)
-                .WillRepeatedly(
-                        [](const renderengine::DisplaySettings& displaySettings,
-                           const std::vector<renderengine::LayerSettings>& /*layerSettings*/,
-                           ANativeWindowBuffer*, base::unique_fd*) -> status_t {
-                            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;
-                        });
+        // Called once with a non-null value to set a framebuffer, and then
+        // again with nullptr to clear it.
+        EXPECT_CALL(*test->mReFrameBuffer, setNativeWindowBuffer(NotNull(), false))
+                .WillOnce(Return(true));
+        EXPECT_CALL(*test->mReFrameBuffer, setNativeWindowBuffer(IsNull(), false))
+                .WillOnce(Return(true));
+
+        EXPECT_CALL(*test->mRenderEngine, checkErrors()).WillRepeatedly(Return());
+        EXPECT_CALL(*test->mRenderEngine, createFramebuffer())
+                .WillOnce(Return(
+                        ByMove(std::unique_ptr<renderengine::Framebuffer>(test->mReFrameBuffer))));
+        EXPECT_CALL(*test->mRenderEngine, bindFrameBuffer(test->mReFrameBuffer)).Times(1);
+        EXPECT_CALL(*test->mRenderEngine, unbindFrameBuffer(test->mReFrameBuffer)).Times(1);
+        EXPECT_CALL(*test->mRenderEngine, clearWithColor(0, 0, 0, 1)).Times(1);
+        EXPECT_CALL(*test->mRenderEngine, flush()).WillOnce(Return(ByMove(base::unique_fd())));
+        EXPECT_CALL(*test->mRenderEngine, finish()).WillOnce(Return(true));
+
+        EXPECT_CALL(*test->mRenderEngine, setOutputDataSpace(_)).Times(1);
+        EXPECT_CALL(*test->mRenderEngine, setDisplayMaxLuminance(DEFAULT_DISPLAY_MAX_LUMINANCE))
+                .Times(1);
+        // This expectation retires on saturation as setViewportAndProjection is
+        // called an extra time for the code path this setup is for.
+        // TODO: Investigate this extra call
+        EXPECT_CALL(*test->mRenderEngine,
+                    setViewportAndProjection(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT,
+                                             Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                                             ui::Transform::ROT_0))
+                .Times(1)
+                .RetiresOnSaturation();
+        EXPECT_CALL(*test->mRenderEngine, disableTexturing()).Times(1);
     }
 
     static void setupNonEmptyFrameCompositionCallExpectations(CompositionTest* test) {
@@ -321,23 +339,31 @@
         EXPECT_CALL(*test->mDisplaySurface, getClientTargetAcquireFence())
                 .WillRepeatedly(ReturnRef(test->mClientTargetAcquireFence));
 
+        EXPECT_CALL(*test->mRenderEngine, setOutputDataSpace(ui::Dataspace::UNKNOWN)).Times(1);
+        EXPECT_CALL(*test->mRenderEngine, setDisplayMaxLuminance(DEFAULT_DISPLAY_MAX_LUMINANCE))
+                .Times(1);
+        EXPECT_CALL(*test->mRenderEngine, setColorTransform(_)).Times(2);
+        // These expectations retire on saturation as the code path these
+        // expectations are for appears to make an extra call to them.
+        // TODO: Investigate this extra call
+        EXPECT_CALL(*test->mRenderEngine,
+                    setViewportAndProjection(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT,
+                                             Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                                             ui::Transform::ROT_0))
+                .Times(1);
+        EXPECT_CALL(*test->mReFrameBuffer, setNativeWindowBuffer(NotNull(), false))
+                .WillOnce(Return(true));
+        EXPECT_CALL(*test->mReFrameBuffer, setNativeWindowBuffer(IsNull(), false))
+                .WillOnce(Return(true));
+        EXPECT_CALL(*test->mRenderEngine, createFramebuffer())
+                .WillOnce(Return(
+                        ByMove(std::unique_ptr<renderengine::Framebuffer>(test->mReFrameBuffer))));
+        EXPECT_CALL(*test->mRenderEngine, bindFrameBuffer(test->mReFrameBuffer)).Times(1);
+        EXPECT_CALL(*test->mRenderEngine, unbindFrameBuffer(test->mReFrameBuffer)).Times(1);
         EXPECT_CALL(*test->mNativeWindow, queueBuffer(_, _)).WillOnce(Return(0));
         EXPECT_CALL(*test->mNativeWindow, dequeueBuffer(_, _))
                 .WillOnce(DoAll(SetArgPointee<0>(test->mNativeWindowBuffer), SetArgPointee<1>(-1),
                                 Return(0)));
-        EXPECT_CALL(*test->mRenderEngine, drawLayers)
-                .WillRepeatedly(
-                        [](const renderengine::DisplaySettings& displaySettings,
-                           const std::vector<renderengine::LayerSettings>& /*layerSettings*/,
-                           ANativeWindowBuffer*, base::unique_fd*) -> status_t {
-                            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;
-                        });
     }
 
     template <typename Case>
@@ -348,6 +374,8 @@
     template <typename Case>
     static void setupRELayerScreenshotCompositionCallExpectations(CompositionTest* test) {
         Case::Layer::setupREScreenshotCompositionCallExpectations(test);
+
+        EXPECT_CALL(*test->mRenderEngine, isCurrent()).WillRepeatedly(Return(true));
     }
 };
 
@@ -359,11 +387,16 @@
     template <typename Case>
     static void setupRELayerCompositionCallExpectations(CompositionTest* test) {
         Case::Layer::setupInsecureRECompositionCallExpectations(test);
+
+        // TODO: Investigate this extra call
+        EXPECT_CALL(*test->mRenderEngine, disableScissor()).Times(1);
     }
 
     template <typename Case>
     static void setupRELayerScreenshotCompositionCallExpectations(CompositionTest* test) {
         Case::Layer::setupInsecureREScreenshotCompositionCallExpectations(test);
+
+        EXPECT_CALL(*test->mRenderEngine, isCurrent()).WillRepeatedly(Return(true));
     }
 };
 
@@ -470,6 +503,7 @@
         bool ignoredRecomputeVisibleRegions;
         layer->latchBuffer(ignoredRecomputeVisibleRegions, 0, Fence::NO_FENCE);
         Mock::VerifyAndClear(test->mRenderEngine);
+        Mock::VerifyAndClear(test->mReImage);
     }
 
     static void setupLayerState(CompositionTest* test, sp<BufferQueueLayer> layer) {
@@ -552,35 +586,33 @@
     }
 
     static void setupREBufferCompositionCommonCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mRenderEngine, drawLayers)
-                .WillOnce([](const renderengine::DisplaySettings& displaySettings,
-                             const std::vector<renderengine::LayerSettings>& layerSettings,
-                             ANativeWindowBuffer*, base::unique_fd*) -> status_t {
-                    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);
-                    // screen capture adds an additional color layer as an alpha
-                    // prefill, so gtet the back layer.
-                    renderengine::LayerSettings layer = layerSettings.back();
-                    EXPECT_THAT(layer.source.buffer.buffer, Not(IsNull()));
-                    EXPECT_THAT(layer.source.buffer.fence, Not(IsNull()));
-                    EXPECT_EQ(renderengine::Buffer::CachingHint::NO_CACHE,
-                              layer.source.buffer.cacheHint);
-                    EXPECT_EQ(DEFAULT_TEXTURE_ID, layer.source.buffer.textureName);
-                    EXPECT_EQ(false, layer.source.buffer.isY410BT2020);
-                    EXPECT_EQ(true, layer.source.buffer.usePremultipliedAlpha);
-                    EXPECT_EQ(false, layer.source.buffer.isOpaque);
-                    EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
-                    EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
-                    EXPECT_EQ(LayerProperties::COLOR[3], layer.alpha);
-                    return NO_ERROR;
-                });
+        EXPECT_CALL(*test->mRenderEngine,
+                    setupLayerBlending(true, false, false,
+                                       half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
+                                             LayerProperties::COLOR[2], LayerProperties::COLOR[3]),
+                                       0.0f))
+                .Times(1);
+
+        EXPECT_CALL(*test->mRenderEngine, createImage())
+                .WillOnce(Return(ByMove(std::unique_ptr<renderengine::Image>(test->mReImage))));
+        EXPECT_CALL(*test->mReImage, setNativeWindowBuffer(_, _)).WillOnce(Return(true));
+        EXPECT_CALL(*test->mRenderEngine, bindExternalTextureImage(DEFAULT_TEXTURE_ID, _)).Times(1);
+        EXPECT_CALL(*test->mRenderEngine, setupLayerTexturing(_)).Times(1);
+        EXPECT_CALL(*test->mRenderEngine, setSourceDataSpace(ui::Dataspace::UNKNOWN)).Times(1);
+        EXPECT_CALL(*test->mRenderEngine, drawMesh(_)).Times(1);
+        EXPECT_CALL(*test->mRenderEngine, disableBlending()).Times(1);
+        EXPECT_CALL(*test->mRenderEngine, setSourceY410BT2020(false)).Times(1);
+        // This call retires on saturation as the code that renders a texture disables the state,
+        // along with a top-level disable to ensure it is disabled for non-buffer layers.
+        EXPECT_CALL(*test->mRenderEngine, disableTexturing()).Times(1).RetiresOnSaturation();
     }
 
     static void setupREBufferCompositionCallExpectations(CompositionTest* test) {
         LayerProperties::setupREBufferCompositionCommonCallExpectations(test);
+
+        // TODO - Investigate and eliminate these differences between display
+        // composition and screenshot composition.
+        EXPECT_CALL(*test->mRenderEngine, disableScissor()).Times(1);
     }
 
     static void setupInsecureREBufferCompositionCallExpectations(CompositionTest* test) {
@@ -595,28 +627,20 @@
         LayerProperties::setupREBufferCompositionCommonCallExpectations(test);
     }
 
+    static void setupREColorCompositionCommonCallExpectations(CompositionTest* test) {
+        EXPECT_CALL(*test->mRenderEngine, disableScissor()).Times(1);
+    }
+
     static void setupREColorCompositionCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mRenderEngine, drawLayers)
-                .WillOnce([](const renderengine::DisplaySettings& displaySettings,
-                             const std::vector<renderengine::LayerSettings>& layerSettings,
-                             ANativeWindowBuffer*, base::unique_fd*) -> status_t {
-                    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);
-                    // screen capture adds an additional color layer as an alpha
-                    // prefill, so get the back layer.
-                    renderengine::LayerSettings layer = layerSettings.back();
-                    EXPECT_THAT(layer.source.buffer.buffer, IsNull());
-                    EXPECT_EQ(half3(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
-                                    LayerProperties::COLOR[2]),
-                              layer.source.solidColor);
-                    EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
-                    EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
-                    EXPECT_EQ(LayerProperties::COLOR[3], layer.alpha);
-                    return NO_ERROR;
-                });
+        EXPECT_CALL(*test->mRenderEngine, setSourceDataSpace(ui::Dataspace::UNKNOWN)).Times(1);
+        EXPECT_CALL(*test->mRenderEngine,
+                    setupLayerBlending(true, false, true,
+                                       half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
+                                             LayerProperties::COLOR[2], LayerProperties::COLOR[3]),
+                                       0.0f))
+                .Times(1);
+        EXPECT_CALL(*test->mRenderEngine, drawMesh(_)).Times(1);
+        EXPECT_CALL(*test->mRenderEngine, disableBlending()).Times(1);
     }
 
     static void setupREColorScreenshotCompositionCallExpectations(CompositionTest* test) {
@@ -656,7 +680,10 @@
         EXPECT_CALL(*test->mComposer, setLayerSurfaceDamage(HWC_DISPLAY, HWC_LAYER, _)).Times(1);
     }
 
-    static void setupREBufferCompositionCommonCallExpectations(CompositionTest* /*test*/) {}
+    static void setupREBufferCompositionCommonCallExpectations(CompositionTest* test) {
+        EXPECT_CALL(*test->mRenderEngine, setupFillWithColor(0, 0, 0, 1)).Times(1);
+        EXPECT_CALL(*test->mRenderEngine, drawMesh(_)).Times(1);
+    }
 };
 
 struct SecureLayerProperties : public BaseLayerProperties<SecureLayerProperties> {
@@ -665,25 +692,25 @@
     static constexpr uint32_t LAYER_FLAGS = ISurfaceComposerClient::eSecure;
 
     static void setupInsecureREBufferCompositionCommonCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mRenderEngine, drawLayers)
-                .WillOnce([](const renderengine::DisplaySettings& displaySettings,
-                             const std::vector<renderengine::LayerSettings>& layerSettings,
-                             ANativeWindowBuffer*, base::unique_fd*) -> status_t {
-                    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);
-                    // screen capture adds an additional color layer as an alpha
-                    // prefill, so get the back layer.
-                    renderengine::LayerSettings layer = layerSettings.back();
-                    EXPECT_THAT(layer.source.buffer.buffer, IsNull());
-                    EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), layer.source.solidColor);
-                    EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
-                    EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
-                    EXPECT_EQ(1.0f, layer.alpha);
-                    return NO_ERROR;
-                });
+        EXPECT_CALL(*test->mRenderEngine, createImage())
+                .WillOnce(Return(ByMove(std::unique_ptr<renderengine::Image>(test->mReImage))));
+        EXPECT_CALL(*test->mReImage, setNativeWindowBuffer(_, _)).WillOnce(Return(true));
+        EXPECT_CALL(*test->mRenderEngine, bindExternalTextureImage(DEFAULT_TEXTURE_ID, _)).Times(1);
+        EXPECT_CALL(*test->mRenderEngine, setupLayerBlackedOut()).Times(1);
+
+        EXPECT_CALL(*test->mRenderEngine,
+                    setupLayerBlending(true, false, false,
+                                       half4(Base::COLOR[0], Base::COLOR[1], Base::COLOR[2],
+                                             Base::COLOR[3]),
+                                       0.0f))
+                .Times(1);
+        EXPECT_CALL(*test->mRenderEngine, setSourceDataSpace(ui::Dataspace::UNKNOWN)).Times(1);
+        EXPECT_CALL(*test->mRenderEngine, drawMesh(_)).Times(1);
+        EXPECT_CALL(*test->mRenderEngine, disableBlending()).Times(1);
+        EXPECT_CALL(*test->mRenderEngine, setSourceY410BT2020(false)).Times(1);
+        // This call retires on saturation as the code that renders a texture disables the state,
+        // along with a top-level disable to ensure it is disabled for non-buffer layers.
+        EXPECT_CALL(*test->mRenderEngine, disableTexturing()).Times(1).RetiresOnSaturation();
     }
 
     static void setupInsecureREBufferCompositionCallExpectations(CompositionTest* test) {
@@ -787,6 +814,7 @@
     }
 
     static void setupRECompositionCallExpectations(CompositionTest* test) {
+        LayerProperties::setupREColorCompositionCommonCallExpectations(test);
         LayerProperties::setupREColorCompositionCallExpectations(test);
     }