Revert "Add ExternalTexture class into RenderEngine interface"

Revert submission 14086921-renderengine-external-tex

Reason for revert: Potential culprit for b/185361988
Reverted Changes:
I7796764e2:Update WaylandRenderSurface to accomodate interfac...
I13904eec4:Update Readback VTS to align with RenderEngine int...
I222c71e6e:Add ExternalTexture class into RenderEngine interf...

Change-Id: I1501890f4861a3df7ce273f1fe2ccdb275e2632c
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index f395ab4..ec39137 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -48,7 +48,6 @@
     name: "librenderengine_sources",
     srcs: [
         "Description.cpp",
-        "ExternalTexture.cpp",
         "Mesh.cpp",
         "RenderEngine.cpp",
         "Texture.cpp",
diff --git a/libs/renderengine/ExternalTexture.cpp b/libs/renderengine/ExternalTexture.cpp
deleted file mode 100644
index eabff58..0000000
--- a/libs/renderengine/ExternalTexture.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <renderengine/ExternalTexture.h>
-#include <renderengine/RenderEngine.h>
-#include <ui/GraphicBuffer.h>
-
-#include "log/log_main.h"
-
-namespace android::renderengine {
-
-ExternalTexture::ExternalTexture(const sp<GraphicBuffer>& buffer, RenderEngine& renderEngine,
-                                 uint32_t usage)
-      : mBuffer(buffer), mRenderEngine(renderEngine) {
-    LOG_ALWAYS_FATAL_IF(buffer == nullptr,
-                        "Attempted to bind a null buffer to an external texture!");
-    // GLESRenderEngine has a separate texture cache for output buffers,
-    if (usage == Usage::WRITEABLE &&
-        (mRenderEngine.getRenderEngineType() == RenderEngine::RenderEngineType::GLES ||
-         mRenderEngine.getRenderEngineType() == RenderEngine::RenderEngineType::THREADED)) {
-        return;
-    }
-    mRenderEngine.mapExternalTextureBuffer(mBuffer, usage & Usage::WRITEABLE);
-}
-
-ExternalTexture::~ExternalTexture() {
-    mRenderEngine.unmapExternalTextureBuffer(mBuffer);
-}
-
-} // namespace android::renderengine
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index d87315f..a2963a7 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -746,8 +746,7 @@
     return;
 }
 
-void GLESRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer,
-                                                bool /*isRenderable*/) {
+void GLESRenderEngine::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
     ATRACE_CALL();
     mImageManager->cacheAsync(buffer, nullptr);
 }
@@ -798,8 +797,8 @@
     return NO_ERROR;
 }
 
-void GLESRenderEngine::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
-    mImageManager->releaseAsync(buffer->getId(), nullptr);
+void GLESRenderEngine::unbindExternalTextureBuffer(uint64_t bufferId) {
+    mImageManager->releaseAsync(bufferId, nullptr);
 }
 
 std::shared_ptr<ImageManager::Barrier> GLESRenderEngine::unbindExternalTextureBufferForTesting(
@@ -1103,7 +1102,7 @@
 
 status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
                                       const std::vector<const LayerSettings*>& layers,
-                                      const std::shared_ptr<ExternalTexture>& buffer,
+                                      const sp<GraphicBuffer>& buffer,
                                       const bool useFramebufferCache, base::unique_fd&& bufferFence,
                                       base::unique_fd* drawFence) {
     ATRACE_CALL();
@@ -1126,7 +1125,7 @@
         return BAD_VALUE;
     }
 
-    validateOutputBufferUsage(buffer->getBuffer());
+    validateOutputBufferUsage(buffer);
 
     std::unique_ptr<BindNativeBufferAsFramebuffer> fbo;
     // Gathering layers that requested blur, we'll need them to decide when to render to an
@@ -1143,13 +1142,11 @@
 
     if (blurLayersSize == 0) {
         fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this,
-                                                              buffer->getBuffer()
-                                                                      .get()
-                                                                      ->getNativeBuffer(),
+                                                              buffer.get()->getNativeBuffer(),
                                                               useFramebufferCache);
         if (fbo->getStatus() != NO_ERROR) {
             ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
-                  buffer->getBuffer()->handle);
+                  buffer->handle);
             checkErrors();
             return fbo->getStatus();
         }
@@ -1160,7 +1157,7 @@
                 mBlurFilter->setAsDrawTarget(display, blurLayers.front()->backgroundBlurRadius);
         if (status != NO_ERROR) {
             ALOGE("Failed to prepare blur filter! Aborting GPU composition for buffer (%p).",
-                  buffer->getBuffer()->handle);
+                  buffer->handle);
             checkErrors();
             return status;
         }
@@ -1197,7 +1194,7 @@
             auto status = mBlurFilter->prepare();
             if (status != NO_ERROR) {
                 ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
-                      buffer->getBuffer()->handle);
+                      buffer->handle);
                 checkErrors("Can't render first blur pass");
                 return status;
             }
@@ -1206,7 +1203,6 @@
                 // Done blurring, time to bind the native FBO and render our blur onto it.
                 fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this,
                                                                       buffer.get()
-                                                                              ->getBuffer()
                                                                               ->getNativeBuffer(),
                                                                       useFramebufferCache);
                 status = fbo->getStatus();
@@ -1219,7 +1215,7 @@
             }
             if (status != NO_ERROR) {
                 ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
-                      buffer->getBuffer()->handle);
+                      buffer->handle);
                 checkErrors("Can't bind native framebuffer");
                 return status;
             }
@@ -1227,7 +1223,7 @@
             status = mBlurFilter->render(blurLayersSize > 1);
             if (status != NO_ERROR) {
                 ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
-                      buffer->getBuffer()->handle);
+                      buffer->handle);
                 checkErrors("Can't render blur filter");
                 return status;
             }
@@ -1254,7 +1250,7 @@
             disableTexture = false;
             isOpaque = layer->source.buffer.isOpaque;
 
-            sp<GraphicBuffer> gBuf = layer->source.buffer.buffer->getBuffer();
+            sp<GraphicBuffer> gBuf = layer->source.buffer.buffer;
             validateInputBufferUsage(gBuf);
             bindExternalTextureBuffer(layer->source.buffer.textureName, gBuf,
                                       layer->source.buffer.fence);
@@ -1278,7 +1274,7 @@
 
             // Do not cache protected EGLImage, protected memory is limited.
             if (gBuf->getUsage() & GRALLOC_USAGE_PROTECTED) {
-                unmapExternalTextureBuffer(gBuf);
+                unbindExternalTextureBuffer(gBuf->getId());
             }
         }
 
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index e7ed9c0..cd7a86b 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -60,14 +60,16 @@
     void primeCache() override;
     void genTextures(size_t count, uint32_t* names) override;
     void deleteTextures(size_t count, uint32_t const* names) override;
+    void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex);
+    void unbindExternalTextureBuffer(uint64_t bufferId) EXCLUDES(mRenderingMutex);
+
     bool isProtected() const override { return mInProtectedContext; }
     bool supportsProtectedContent() const override;
     bool useProtectedContext(bool useProtectedContext) override;
     status_t drawLayers(const DisplaySettings& display,
                         const std::vector<const LayerSettings*>& layers,
-                        const std::shared_ptr<ExternalTexture>& buffer,
-                        const bool useFramebufferCache, base::unique_fd&& bufferFence,
-                        base::unique_fd* drawFence) override;
+                        const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
+                        base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
     bool cleanupPostRender(CleanupMode mode) override;
     int getContextPriority() override;
     bool supportsBackgroundBlur() override { return mBlurFilter != nullptr; }
@@ -103,9 +105,6 @@
             EXCLUDES(mFramebufferImageCacheMutex);
     size_t getMaxTextureSize() const override;
     size_t getMaxViewportDims() const override;
-    void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable)
-            EXCLUDES(mRenderingMutex);
-    void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex);
 
 private:
     friend class BindNativeBufferAsFramebuffer;
diff --git a/libs/renderengine/include/renderengine/ExternalTexture.h b/libs/renderengine/include/renderengine/ExternalTexture.h
deleted file mode 100644
index 07f0833..0000000
--- a/libs/renderengine/include/renderengine/ExternalTexture.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <android-base/macros.h>
-#include <ui/GraphicBuffer.h>
-
-namespace android::renderengine {
-
-class RenderEngine;
-
-/**
- * Manages GPU image resources on behalf of clients using RenderEngine.
- *
- * Clients of RenderEngine are required to wrap their GraphicBuffer objects as an ExternalTexture,
- * which is then mapped into GPU resources required by RenderEngine. When a client no longer needs
- * to use the GraphicBuffer as input into RenderEngine::drawLayers, then the client should delete
- * their ExternalTexture so that resources may be freed.
- */
-class ExternalTexture {
-public:
-    // Usage specifies the rendering intent for the buffer.
-    enum Usage : uint32_t {
-        // When a buffer is not READABLE but is WRITEABLE, then GLESRenderEngine will use that as a
-        // hint to load the buffer into a separate cache
-        READABLE = 1 << 0,
-
-        // The buffer needs to be mapped as a 2D texture if set, otherwise must be mapped as an
-        // external texture
-        WRITEABLE = 1 << 1,
-    };
-    // Creates an ExternalTexture for the provided buffer and RenderEngine instance, with the given
-    // usage hint of type Usage.
-    ExternalTexture(const sp<GraphicBuffer>& buffer, RenderEngine& renderEngine, uint32_t usage);
-
-    ~ExternalTexture();
-
-    // Retrieves the buffer that is bound to this texture.
-    const sp<GraphicBuffer>& getBuffer() const { return mBuffer; }
-
-private:
-    sp<GraphicBuffer> mBuffer;
-    RenderEngine& mRenderEngine;
-    DISALLOW_COPY_AND_ASSIGN(ExternalTexture);
-};
-
-} // namespace android::renderengine
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index c54c5ba..7661233 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -16,9 +16,11 @@
 
 #pragma once
 
+#include <iosfwd>
+
 #include <math/mat4.h>
 #include <math/vec3.h>
-#include <renderengine/ExternalTexture.h>
+#include <renderengine/Texture.h>
 #include <ui/BlurRegion.h>
 #include <ui/Fence.h>
 #include <ui/FloatRect.h>
@@ -29,8 +31,6 @@
 #include <ui/StretchEffect.h>
 #include <ui/Transform.h>
 
-#include <iosfwd>
-
 namespace android {
 namespace renderengine {
 
@@ -39,7 +39,7 @@
     // Buffer containing the image that we will render.
     // If buffer == nullptr, then the rest of the fields in this struct will be
     // ignored.
-    std::shared_ptr<ExternalTexture> buffer = nullptr;
+    sp<GraphicBuffer> buffer = nullptr;
 
     // Fence that will fire when the buffer is ready to be bound.
     sp<Fence> fence = nullptr;
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index c8a0f0a..8dd98c3 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -17,20 +17,19 @@
 #ifndef SF_RENDERENGINE_H_
 #define SF_RENDERENGINE_H_
 
+#include <stdint.h>
+#include <sys/types.h>
+#include <memory>
+
 #include <android-base/unique_fd.h>
 #include <math/mat4.h>
 #include <renderengine/DisplaySettings.h>
-#include <renderengine/ExternalTexture.h>
 #include <renderengine/Framebuffer.h>
 #include <renderengine/Image.h>
 #include <renderengine/LayerSettings.h>
-#include <stdint.h>
-#include <sys/types.h>
 #include <ui/GraphicTypes.h>
 #include <ui/Transform.h>
 
-#include <memory>
-
 /**
  * Allows to set RenderEngine backend to GLES (default) or SkiaGL (NOT yet supported).
  */
@@ -52,7 +51,6 @@
 
 namespace renderengine {
 
-class ExternalTexture;
 class Image;
 class Mesh;
 class Texture;
@@ -106,6 +104,23 @@
 
     virtual void genTextures(size_t count, uint32_t* names) = 0;
     virtual void deleteTextures(size_t count, uint32_t const* names) = 0;
+    // Caches Image resources for this buffer, but does not bind the buffer to
+    // a particular texture.
+    // Note that work is deferred to an additional thread, i.e. this call
+    // is made asynchronously, but the caller can expect that cache/unbind calls
+    // are performed in a manner that's conflict serializable, i.e. unbinding
+    // a buffer should never occur before binding the buffer if the caller
+    // called {bind, cache}ExternalTextureBuffer before calling unbind.
+    virtual void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) = 0;
+    // Removes internal resources referenced by the bufferId. This method should be
+    // invoked when the caller will no longer hold a reference to a GraphicBuffer
+    // and needs to clean up its resources.
+    // Note that work is deferred to an additional thread, i.e. this call
+    // is made asynchronously, but the caller can expect that cache/unbind calls
+    // are performed in a manner that's conflict serializable, i.e. unbinding
+    // a buffer should never occur before binding the buffer if the caller
+    // called {bind, cache}ExternalTextureBuffer before calling unbind.
+    virtual void unbindExternalTextureBuffer(uint64_t bufferId) = 0;
 
     enum class CleanupMode {
         CLEAN_OUTPUT_RESOURCES,
@@ -176,9 +191,8 @@
     // now, this always returns NO_ERROR.
     virtual status_t drawLayers(const DisplaySettings& display,
                                 const std::vector<const LayerSettings*>& layers,
-                                const std::shared_ptr<ExternalTexture>& buffer,
-                                const bool useFramebufferCache, base::unique_fd&& bufferFence,
-                                base::unique_fd* drawFence) = 0;
+                                const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
+                                base::unique_fd&& bufferFence, base::unique_fd* drawFence) = 0;
     virtual void cleanFramebufferCache() = 0;
     // Returns the priority this context was actually created with. Note: this may not be
     // the same as specified at context creation time, due to implementation limits on the
@@ -199,31 +213,6 @@
     static void validateOutputBufferUsage(const sp<GraphicBuffer>&);
 
 protected:
-    // Maps GPU resources for this buffer.
-    // Note that work may be deferred to an additional thread, i.e. this call
-    // is made asynchronously, but the caller can expect that map/unmap calls
-    // are performed in a manner that's conflict serializable, i.e. unmapping
-    // a buffer should never occur before binding the buffer if the caller
-    // called mapExternalTextureBuffer before calling unmap.
-    // Note also that if the buffer contains protected content, then mapping those GPU resources may
-    // be deferred until the buffer is really used for drawing. This is because typical SoCs that
-    // support protected memory only support a limited amount, so optimisitically mapping protected
-    // memory may be too burdensome. If a buffer contains protected content and the RenderEngine
-    // implementation supports protected context, then GPU resources may be mapped into both the
-    // protected and unprotected contexts.
-    // If the buffer may ever be written to by RenderEngine, then isRenderable must be true.
-    virtual void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) = 0;
-    // Unmaps GPU resources used by this buffer. This method should be
-    // invoked when the caller will no longer hold a reference to a GraphicBuffer
-    // and needs to clean up its resources.
-    // Note that if there are multiple callers holding onto the same buffer, then the buffer's
-    // resources may be internally ref-counted to guard against use-after-free errors. Note that
-    // work may be deferred to an additional thread, i.e. this call is expected to be made
-    // asynchronously, but the caller can expect that map/unmap calls are performed in a manner
-    // that's conflict serializable, i.e. unmap a buffer should never occur before binding the
-    // buffer if the caller called mapExternalTextureBuffer before calling unmap.
-    virtual void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) = 0;
-    friend class ExternalTexture;
     friend class threaded::RenderEngineThreaded;
     const RenderEngineType mRenderEngineType;
 };
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index 27dbd1e..228553d 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -39,6 +39,8 @@
     MOCK_METHOD1(dump, void(std::string&));
     MOCK_METHOD2(genTextures, void(size_t, uint32_t*));
     MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*));
+    MOCK_METHOD1(cacheExternalTextureBuffer, void(const sp<GraphicBuffer>&));
+    MOCK_METHOD1(unbindExternalTextureBuffer, void(uint64_t));
     MOCK_METHOD1(drawMesh, void(const renderengine::Mesh&));
     MOCK_CONST_METHOD0(getMaxTextureSize, size_t());
     MOCK_CONST_METHOD0(getMaxViewportDims, size_t());
@@ -48,17 +50,12 @@
     MOCK_METHOD1(cleanupPostRender, bool(CleanupMode mode));
     MOCK_METHOD6(drawLayers,
                  status_t(const DisplaySettings&, const std::vector<const LayerSettings*>&,
-                          const std::shared_ptr<ExternalTexture>&, const bool, base::unique_fd&&,
+                          const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
                           base::unique_fd*));
     MOCK_METHOD0(cleanFramebufferCache, void());
     MOCK_METHOD0(getContextPriority, int());
     MOCK_METHOD0(supportsBackgroundBlur, bool());
     MOCK_METHOD1(onPrimaryDisplaySizeChanged, void(ui::Size));
-
-protected:
-    // mock renderengine still needs to implement these, but callers should never need to call them.
-    void mapExternalTextureBuffer(const sp<GraphicBuffer>&, bool) {}
-    void unmapExternalTextureBuffer(const sp<GraphicBuffer>&) {}
 };
 
 } // namespace mock
diff --git a/libs/renderengine/skia/AutoBackendTexture.h b/libs/renderengine/skia/AutoBackendTexture.h
index 2d61cf8..bb75878 100644
--- a/libs/renderengine/skia/AutoBackendTexture.h
+++ b/libs/renderengine/skia/AutoBackendTexture.h
@@ -21,9 +21,9 @@
 #include <SkImage.h>
 #include <SkSurface.h>
 #include <sys/types.h>
-#include <ui/GraphicTypes.h>
 
 #include "android-base/macros.h"
+#include "ui/GraphicTypes.h"
 
 namespace android {
 namespace renderengine {
@@ -41,18 +41,13 @@
     // of shared ownership with Skia objects, so we wrap it here instead.
     class LocalRef {
     public:
-        LocalRef(AutoBackendTexture* texture) { setTexture(texture); }
+        LocalRef() {}
 
         ~LocalRef() {
             // Destroying the texture is the same as setting it to null
             setTexture(nullptr);
         }
 
-        AutoBackendTexture* getTexture() const { return mTexture; }
-
-        DISALLOW_COPY_AND_ASSIGN(LocalRef);
-
-    private:
         // Sets the texture to locally ref-track.
         void setTexture(AutoBackendTexture* texture) {
             if (mTexture != nullptr) {
@@ -64,6 +59,12 @@
                 mTexture->ref();
             }
         }
+
+        AutoBackendTexture* getTexture() const { return mTexture; }
+
+        DISALLOW_COPY_AND_ASSIGN(LocalRef);
+
+    private:
         AutoBackendTexture* mTexture = nullptr;
     };
 
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index 1c2b2fc..1db20c0 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -46,7 +46,7 @@
 } // namespace
 
 static void drawShadowLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
-                             const std::shared_ptr<ExternalTexture>& dstTexture) {
+                             sp<GraphicBuffer> dstBuffer) {
     // Somewhat arbitrary dimensions, but on screen and slightly shorter, based
     // on actual use.
     FloatRect rect(0, 0, display.physicalDisplay.width(), display.physicalDisplay.height() - 30);
@@ -73,7 +73,7 @@
 
     auto layers = std::vector<const LayerSettings*>{&layer};
     // The identity matrix will generate the fast shader
-    renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, base::unique_fd(),
+    renderengine->drawLayers(display, layers, dstBuffer, kUseFrameBufferCache, base::unique_fd(),
                              nullptr);
     // This matrix, which has different scales for x and y, will
     // generate the slower (more general case) version, which has variants for translucent
@@ -86,14 +86,13 @@
     // clang-format on
     for (auto translucent : {false, true}) {
         layer.shadow.casterIsTranslucent = translucent;
-        renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
+        renderengine->drawLayers(display, layers, dstBuffer, kUseFrameBufferCache,
                                  base::unique_fd(), nullptr);
     }
 }
 
 static void drawImageLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
-                            const std::shared_ptr<ExternalTexture>& dstTexture,
-                            const std::shared_ptr<ExternalTexture>& srcTexture) {
+                            sp<GraphicBuffer> dstBuffer, sp<GraphicBuffer> srcBuffer) {
     const Rect& displayRect = display.physicalDisplay;
     FloatRect rect(0, 0, displayRect.width(), displayRect.height());
     LayerSettings layer{
@@ -104,7 +103,7 @@
                     },
             .source = PixelSource{.buffer =
                                           Buffer{
-                                                  .buffer = srcTexture,
+                                                  .buffer = srcBuffer,
                                                   .maxMasteringLuminance = 1000.f,
                                                   .maxContentLuminance = 1000.f,
                                           }},
@@ -127,7 +126,7 @@
                 layer.source.buffer.isOpaque = isOpaque;
                 for (auto alpha : {half(.23999f), half(1.0f)}) {
                     layer.alpha = alpha;
-                    renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
+                    renderengine->drawLayers(display, layers, dstBuffer, kUseFrameBufferCache,
                                              base::unique_fd(), nullptr);
                 }
             }
@@ -136,7 +135,7 @@
 }
 
 static void drawSolidLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
-                            const std::shared_ptr<ExternalTexture>& dstTexture) {
+                            sp<GraphicBuffer> dstBuffer) {
     const Rect& displayRect = display.physicalDisplay;
     FloatRect rect(0, 0, displayRect.width(), displayRect.height());
     LayerSettings layer{
@@ -144,11 +143,11 @@
                     Geometry{
                             .boundaries = rect,
                     },
+            .alpha = 1,
             .source =
                     PixelSource{
                             .solidColor = half3(0.1f, 0.2f, 0.3f),
                     },
-            .alpha = 1,
     };
 
     auto layers = std::vector<const LayerSettings*>{&layer};
@@ -156,14 +155,14 @@
         layer.geometry.positionTransform = transform;
         for (float roundedCornersRadius : {0.0f, 0.05f, 50.f}) {
             layer.geometry.roundedCornersRadius = roundedCornersRadius;
-            renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
+            renderengine->drawLayers(display, layers, dstBuffer, kUseFrameBufferCache,
                                      base::unique_fd(), nullptr);
         }
     }
 }
 
 static void drawBlurLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
-                           const std::shared_ptr<ExternalTexture>& dstTexture) {
+                           sp<GraphicBuffer> dstBuffer) {
     const Rect& displayRect = display.physicalDisplay;
     FloatRect rect(0, 0, displayRect.width(), displayRect.height());
     LayerSettings layer{
@@ -177,7 +176,7 @@
     auto layers = std::vector<const LayerSettings*>{&layer};
     for (int radius : {9, 60}) {
         layer.backgroundBlurRadius = radius;
-        renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
+        renderengine->drawLayers(display, layers, dstBuffer, kUseFrameBufferCache,
                                  base::unique_fd(), nullptr);
     }
 }
@@ -215,9 +214,6 @@
     sp<GraphicBuffer> dstBuffer =
             new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1,
                               usage, "primeShaderCache_dst");
-
-    const auto dstTexture = std::make_shared<ExternalTexture>(dstBuffer, *renderengine,
-                                                              ExternalTexture::Usage::WRITEABLE);
     // This buffer will be the source for the call to drawImageLayers. Draw
     // something to it as a placeholder for what an app draws. We should draw
     // something, but the details are not important. Make use of the shadow layer drawing step
@@ -226,16 +222,11 @@
             new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1,
                               usage, "drawImageLayer_src");
 
-    const auto srcTexture =
-            std::make_shared<ExternalTexture>(srcBuffer, *renderengine,
-                                              ExternalTexture::Usage::READABLE |
-                                                      ExternalTexture::Usage::WRITEABLE);
-
-    drawSolidLayers(renderengine, display, dstTexture);
-    drawShadowLayers(renderengine, display, srcTexture);
-    drawBlurLayers(renderengine, display, dstTexture);
+    drawSolidLayers(renderengine, display, dstBuffer);
+    drawShadowLayers(renderengine, display, srcBuffer);
+    drawBlurLayers(renderengine, display, dstBuffer);
     // The majority of shaders are related to sampling images.
-    drawImageLayers(renderengine, display, dstTexture, srcTexture);
+    drawImageLayers(renderengine, display, dstBuffer, srcBuffer);
 
     // should be the same as AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
     const int64_t usageExternal = GRALLOC_USAGE_HW_TEXTURE;
@@ -243,12 +234,12 @@
     sp<GraphicBuffer> externalBuffer =
             new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1,
                               usageExternal, "primeShaderCache_external");
-    const auto externalTexture =
-            std::make_shared<ExternalTexture>(externalBuffer, *renderengine,
-                                              ExternalTexture::Usage::READABLE);
     // TODO(b/184665179) doubles number of image shader compilations, but only somewhere
     // between 6 and 8 will occur in real uses.
-    drawImageLayers(renderengine, display, dstTexture, externalTexture);
+    drawImageLayers(renderengine, display, dstBuffer, externalBuffer);
+    renderengine->unbindExternalTextureBuffer(externalBuffer->getId());
+
+    renderengine->unbindExternalTextureBuffer(srcBuffer->getId());
 
     const nsecs_t timeAfter = systemTime();
     const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index e781584..3b2c7e5 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -329,6 +329,8 @@
 }
 
 SkiaGLRenderEngine::~SkiaGLRenderEngine() {
+    cleanFramebufferCache();
+
     std::lock_guard<std::mutex> lock(mRenderingMutex);
     if (mBlurFilter) {
         delete mBlurFilter;
@@ -482,8 +484,7 @@
             sourceTransfer != destTransfer;
 }
 
-void SkiaGLRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer,
-                                                  bool isRenderable) {
+void SkiaGLRenderEngine::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
     // Only run this if RE is running on its own thread. This way the access to GL
     // operations is guaranteed to be happening on the same thread.
     if (mRenderEngineType != RenderEngineType::SKIA_GL_THREADED) {
@@ -504,41 +505,25 @@
     auto& cache = mInProtectedContext ? mProtectedTextureCache : mTextureCache;
 
     std::lock_guard<std::mutex> lock(mRenderingMutex);
-    mGraphicBufferExternalRefs[buffer->getId()]++;
-
-    if (const auto& iter = cache.find(buffer->getId()); iter == cache.end()) {
+    auto iter = cache.find(buffer->getId());
+    if (iter != cache.end()) {
+        ALOGV("Texture already exists in cache.");
+    } else {
         std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef =
-                std::make_shared<AutoBackendTexture::LocalRef>(
-                        new AutoBackendTexture(grContext.get(), buffer->toAHardwareBuffer(),
-                                               isRenderable));
+                std::make_shared<AutoBackendTexture::LocalRef>();
+        imageTextureRef->setTexture(
+                new AutoBackendTexture(grContext.get(), buffer->toAHardwareBuffer(), false));
         cache.insert({buffer->getId(), imageTextureRef});
     }
     // restore the original state of the protected context if necessary
     useProtectedContext(protectedContextState);
 }
 
-void SkiaGLRenderEngine::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
+void SkiaGLRenderEngine::unbindExternalTextureBuffer(uint64_t bufferId) {
     ATRACE_CALL();
     std::lock_guard<std::mutex> lock(mRenderingMutex);
-    if (const auto& iter = mGraphicBufferExternalRefs.find(buffer->getId());
-        iter != mGraphicBufferExternalRefs.end()) {
-        if (iter->second == 0) {
-            ALOGW("Attempted to unmap GraphicBuffer <id: %" PRId64
-                  "> from RenderEngine texture, but the "
-                  "ref count was already zero!",
-                  buffer->getId());
-            mGraphicBufferExternalRefs.erase(buffer->getId());
-            return;
-        }
-
-        iter->second--;
-
-        if (iter->second == 0) {
-            mTextureCache.erase(buffer->getId());
-            mProtectedTextureCache.erase(buffer->getId());
-            mGraphicBufferExternalRefs.erase(buffer->getId());
-        }
-    }
+    mTextureCache.erase(bufferId);
+    mProtectedTextureCache.erase(bufferId);
 }
 
 sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(sk_sp<SkShader> shader,
@@ -636,8 +621,8 @@
 
 status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
                                         const std::vector<const LayerSettings*>& layers,
-                                        const std::shared_ptr<ExternalTexture>& buffer,
-                                        const bool /*useFramebufferCache*/,
+                                        const sp<GraphicBuffer>& buffer,
+                                        const bool useFramebufferCache,
                                         base::unique_fd&& bufferFence, base::unique_fd* drawFence) {
     ATRACE_NAME("SkiaGL::drawLayers");
 
@@ -660,18 +645,32 @@
         return BAD_VALUE;
     }
 
-    validateOutputBufferUsage(buffer->getBuffer());
+    validateOutputBufferUsage(buffer);
 
     auto grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
     auto& cache = mInProtectedContext ? mProtectedTextureCache : mTextureCache;
+    AHardwareBuffer_Desc bufferDesc;
+    AHardwareBuffer_describe(buffer->toAHardwareBuffer(), &bufferDesc);
 
-    std::shared_ptr<AutoBackendTexture::LocalRef> surfaceTextureRef;
-    if (const auto& it = cache.find(buffer->getBuffer()->getId()); it != cache.end()) {
-        surfaceTextureRef = it->second;
-    } else {
-        surfaceTextureRef = std::make_shared<AutoBackendTexture::LocalRef>(
-                new AutoBackendTexture(grContext.get(), buffer->getBuffer()->toAHardwareBuffer(),
-                                       true));
+    std::shared_ptr<AutoBackendTexture::LocalRef> surfaceTextureRef = nullptr;
+    if (useFramebufferCache) {
+        auto iter = cache.find(buffer->getId());
+        if (iter != cache.end()) {
+            ALOGV("Cache hit!");
+            ATRACE_NAME("Cache hit");
+            surfaceTextureRef = iter->second;
+        }
+    }
+
+    if (surfaceTextureRef == nullptr || surfaceTextureRef->getTexture() == nullptr) {
+        ATRACE_NAME("Cache miss");
+        surfaceTextureRef = std::make_shared<AutoBackendTexture::LocalRef>();
+        surfaceTextureRef->setTexture(
+                new AutoBackendTexture(grContext.get(), buffer->toAHardwareBuffer(), true));
+        if (useFramebufferCache) {
+            ALOGD("Adding to cache");
+            cache.insert({buffer->getId(), surfaceTextureRef});
+        }
     }
 
     const ui::Dataspace dstDataspace =
@@ -877,22 +876,18 @@
         SkPaint paint;
         if (layer->source.buffer.buffer) {
             ATRACE_NAME("DrawImage");
-            validateInputBufferUsage(layer->source.buffer.buffer->getBuffer());
+            validateInputBufferUsage(layer->source.buffer.buffer);
             const auto& item = layer->source.buffer;
             std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef = nullptr;
-
-            if (const auto& iter = cache.find(item.buffer->getBuffer()->getId());
-                iter != cache.end()) {
+            auto iter = cache.find(item.buffer->getId());
+            if (iter != cache.end()) {
                 imageTextureRef = iter->second;
             } else {
-                // If we didn't find the image in the cache, then create a local ref but don't cache
-                // it. If we're using skia, we're guaranteed to run on a dedicated GPU thread so if
-                // we didn't find anything in the cache then we intentionally did not cache this
-                // buffer's resources.
-                imageTextureRef = std::make_shared<AutoBackendTexture::LocalRef>(
-                        new AutoBackendTexture(grContext.get(),
-                                               item.buffer->getBuffer()->toAHardwareBuffer(),
-                                               false));
+                imageTextureRef = std::make_shared<AutoBackendTexture::LocalRef>();
+                imageTextureRef->setTexture(new AutoBackendTexture(grContext.get(),
+                                                                   item.buffer->toAHardwareBuffer(),
+                                                                   false));
+                cache.insert({item.buffer->getId(), imageTextureRef});
             }
 
             sk_sp<SkImage> image =
@@ -1205,6 +1200,15 @@
     return eglCreatePbufferSurface(display, placeholderConfig, attributes.data());
 }
 
+void SkiaGLRenderEngine::cleanFramebufferCache() {
+    // TODO(b/180767535) Remove this method and use b/180767535 instead, which would allow
+    // SF to control texture lifecycle more tightly rather than through custom hooks into RE.
+    std::lock_guard<std::mutex> lock(mRenderingMutex);
+    mRuntimeEffects.clear();
+    mProtectedTextureCache.clear();
+    mTextureCache.clear();
+}
+
 int SkiaGLRenderEngine::getContextPriority() {
     int value;
     eglQueryContext(mEGLDisplay, mEGLContext, EGL_CONTEXT_PRIORITY_LEVEL_IMG, &value);
@@ -1277,12 +1281,6 @@
         StringAppendF(&result, "Skia's Wrapped Objects:\n");
         gpuReporter.logOutput(result, true);
 
-        StringAppendF(&result, "RenderEngine tracked buffers: %zu\n",
-                      mGraphicBufferExternalRefs.size());
-        StringAppendF(&result, "Dumping buffer ids...\n");
-        for (const auto& [id, refCounts] : mGraphicBufferExternalRefs) {
-            StringAppendF(&result, "- 0x%" PRIx64 " - %d refs \n", id, refCounts);
-        }
         StringAppendF(&result, "RenderEngine AHB/BackendTexture cache size: %zu\n",
                       mTextureCache.size());
         StringAppendF(&result, "Dumping buffer ids...\n");
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index e71c560..8e77c16 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -23,7 +23,6 @@
 #include <GrDirectContext.h>
 #include <SkSurface.h>
 #include <android-base/thread_annotations.h>
-#include <renderengine/ExternalTexture.h>
 #include <renderengine/RenderEngine.h>
 #include <sys/types.h>
 
@@ -53,12 +52,13 @@
     ~SkiaGLRenderEngine() override EXCLUDES(mRenderingMutex);
 
     void primeCache() override;
+    void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
+    void unbindExternalTextureBuffer(uint64_t bufferId) override;
     status_t drawLayers(const DisplaySettings& display,
                         const std::vector<const LayerSettings*>& layers,
-                        const std::shared_ptr<ExternalTexture>& buffer,
-                        const bool useFramebufferCache, base::unique_fd&& bufferFence,
-                        base::unique_fd* drawFence) override;
-    void cleanFramebufferCache() override {}
+                        const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
+                        base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
+    void cleanFramebufferCache() override;
     int getContextPriority() override;
     bool isProtected() const override { return mInProtectedContext; }
     bool supportsProtectedContent() const override;
@@ -72,8 +72,6 @@
     void dump(std::string& result) override;
     size_t getMaxTextureSize() const override;
     size_t getMaxViewportDims() const override;
-    void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) override;
-    void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
 
 private:
     static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
@@ -116,9 +114,7 @@
     const PixelFormat mDefaultPixelFormat;
     const bool mUseColorManagement;
 
-    // Number of external holders of ExternalTexture references, per GraphicBuffer ID.
-    std::unordered_map<uint64_t, int32_t> mGraphicBufferExternalRefs GUARDED_BY(mRenderingMutex);
-    // Cache of GL textures that we'll store per GraphicBuffer ID, sliced by GPU context.
+    // Cache of GL textures that we'll store per GraphicBuffer ID
     std::unordered_map<uint64_t, std::shared_ptr<AutoBackendTexture::LocalRef>> mTextureCache
             GUARDED_BY(mRenderingMutex);
     std::unordered_map<uint64_t, std::shared_ptr<AutoBackendTexture::LocalRef>>
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index 308c5ff..51ef088 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -42,12 +42,15 @@
     virtual void primeCache() override{};
     virtual void genTextures(size_t /*count*/, uint32_t* /*names*/) override{};
     virtual void deleteTextures(size_t /*count*/, uint32_t const* /*names*/) override{};
+    virtual void cacheExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/){};
+    virtual void unbindExternalTextureBuffer(uint64_t /*bufferId*/){};
+
     virtual bool isProtected() const override { return false; } // mInProtectedContext; }
     virtual bool supportsProtectedContent() const override { return false; };
     virtual bool useProtectedContext(bool /*useProtectedContext*/) override { return false; };
     virtual status_t drawLayers(const DisplaySettings& /*display*/,
                                 const std::vector<const LayerSettings*>& /*layers*/,
-                                const std::shared_ptr<ExternalTexture>& /*buffer*/,
+                                const sp<GraphicBuffer>& /*buffer*/,
                                 const bool /*useFramebufferCache*/,
                                 base::unique_fd&& /*bufferFence*/,
                                 base::unique_fd* /*drawFence*/) override {
@@ -57,11 +60,6 @@
     virtual int getContextPriority() override { return 0; }
     virtual void assertShadersCompiled(int numShaders) {}
     virtual int reportShadersCompiled() { return 0; }
-
-protected:
-    virtual void mapExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/,
-                                          bool /*isRenderable*/) override;
-    virtual void unmapExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/) override;
 };
 
 } // namespace skia
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index d63c88b..7846156 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -24,7 +24,6 @@
 
 #include <cutils/properties.h>
 #include <gtest/gtest.h>
-#include <renderengine/ExternalTexture.h>
 #include <renderengine/RenderEngine.h>
 #include <sync/sync.h>
 #include <ui/PixelFormat.h>
@@ -161,42 +160,27 @@
 
 class RenderEngineTest : public ::testing::TestWithParam<std::shared_ptr<RenderEngineFactory>> {
 public:
-    std::shared_ptr<renderengine::ExternalTexture> allocateDefaultBuffer() {
-        return std::make_shared<
-                renderengine::
-                        ExternalTexture>(new GraphicBuffer(DEFAULT_DISPLAY_WIDTH,
-                                                           DEFAULT_DISPLAY_HEIGHT,
-                                                           HAL_PIXEL_FORMAT_RGBA_8888, 1,
-                                                           GRALLOC_USAGE_SW_READ_OFTEN |
-                                                                   GRALLOC_USAGE_SW_WRITE_OFTEN |
-                                                                   GRALLOC_USAGE_HW_RENDER |
-                                                                   GRALLOC_USAGE_HW_TEXTURE,
-                                                           "output"),
-                                         *mRE,
-                                         renderengine::ExternalTexture::Usage::READABLE |
-                                                 renderengine::ExternalTexture::Usage::WRITEABLE);
+    static sp<GraphicBuffer> allocateDefaultBuffer() {
+        return new GraphicBuffer(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT,
+                                 HAL_PIXEL_FORMAT_RGBA_8888, 1,
+                                 GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
+                                         GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE,
+                                 "output");
     }
 
     // Allocates a 1x1 buffer to fill with a solid color
-    std::shared_ptr<renderengine::ExternalTexture> allocateSourceBuffer(uint32_t width,
-                                                                        uint32_t height) {
-        return std::make_shared<
-                renderengine::
-                        ExternalTexture>(new GraphicBuffer(width, height,
-                                                           HAL_PIXEL_FORMAT_RGBA_8888, 1,
-                                                           GRALLOC_USAGE_SW_READ_OFTEN |
-                                                                   GRALLOC_USAGE_SW_WRITE_OFTEN |
-                                                                   GRALLOC_USAGE_HW_TEXTURE,
-                                                           "input"),
-                                         *mRE,
-                                         renderengine::ExternalTexture::Usage::READABLE |
-                                                 renderengine::ExternalTexture::Usage::WRITEABLE);
+    static sp<GraphicBuffer> allocateSourceBuffer(uint32_t width, uint32_t height) {
+        return new GraphicBuffer(width, height, HAL_PIXEL_FORMAT_RGBA_8888, 1,
+                                 GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
+                                         GRALLOC_USAGE_HW_TEXTURE,
+                                 "input");
     }
 
     RenderEngineTest() {
         const ::testing::TestInfo* const test_info =
                 ::testing::UnitTest::GetInstance()->current_test_info();
         ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+        mBuffer = allocateDefaultBuffer();
     }
 
     ~RenderEngineTest() {
@@ -227,21 +211,20 @@
         }
 
         uint8_t* pixels;
-        mBuffer->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
-                                   reinterpret_cast<void**>(&pixels));
+        mBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+                      reinterpret_cast<void**>(&pixels));
 
         file << "P6\n";
-        file << mBuffer->getBuffer()->getWidth() << "\n";
-        file << mBuffer->getBuffer()->getHeight() << "\n";
+        file << mBuffer->getWidth() << "\n";
+        file << mBuffer->getHeight() << "\n";
         file << 255 << "\n";
 
-        std::vector<uint8_t> outBuffer(mBuffer->getBuffer()->getWidth() *
-                                       mBuffer->getBuffer()->getHeight() * 3);
+        std::vector<uint8_t> outBuffer(mBuffer->getWidth() * mBuffer->getHeight() * 3);
         auto outPtr = reinterpret_cast<uint8_t*>(outBuffer.data());
 
-        for (int32_t j = 0; j < mBuffer->getBuffer()->getHeight(); j++) {
-            const uint8_t* src = pixels + (mBuffer->getBuffer()->getStride() * j) * 4;
-            for (int32_t i = 0; i < mBuffer->getBuffer()->getWidth(); i++) {
+        for (int32_t j = 0; j < mBuffer->getHeight(); j++) {
+            const uint8_t* src = pixels + (mBuffer->getStride() * j) * 4;
+            for (int32_t i = 0; i < mBuffer->getWidth(); i++) {
                 // Only copy R, G and B components
                 outPtr[0] = src[0];
                 outPtr[1] = src[1];
@@ -252,7 +235,7 @@
             }
         }
         file.write(reinterpret_cast<char*>(outBuffer.data()), outBuffer.size());
-        mBuffer->getBuffer()->unlock();
+        mBuffer->unlock();
     }
 
     void expectBufferColor(const Region& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
@@ -279,13 +262,13 @@
     void expectBufferColor(const Rect& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
                            std::function<bool(const uint8_t* a, const uint8_t* b)> colorCompare) {
         uint8_t* pixels;
-        mBuffer->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
-                                   reinterpret_cast<void**>(&pixels));
+        mBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+                      reinterpret_cast<void**>(&pixels));
         int32_t maxFails = 10;
         int32_t fails = 0;
         for (int32_t j = 0; j < region.getHeight(); j++) {
-            const uint8_t* src = pixels +
-                    (mBuffer->getBuffer()->getStride() * (region.top + j) + region.left) * 4;
+            const uint8_t* src =
+                    pixels + (mBuffer->getStride() * (region.top + j) + region.left) * 4;
             for (int32_t i = 0; i < region.getWidth(); i++) {
                 const uint8_t expected[4] = {r, g, b, a};
                 bool equal = colorCompare(src, expected);
@@ -306,7 +289,7 @@
                 break;
             }
         }
-        mBuffer->getBuffer()->unlock();
+        mBuffer->unlock();
     }
 
     void expectAlpha(const Rect& rect, uint8_t a) {
@@ -404,6 +387,7 @@
         base::unique_fd fence;
         status_t status =
                 mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fence);
+        mCurrentBuffer = mBuffer;
 
         int fd = fence.release();
         if (fd >= 0) {
@@ -413,7 +397,7 @@
 
         ASSERT_EQ(NO_ERROR, status);
         if (layers.size() > 0 && mGLESRE != nullptr) {
-            ASSERT_TRUE(mGLESRE->isFramebufferImageCachedForTesting(mBuffer->getBuffer()->getId()));
+            ASSERT_TRUE(mGLESRE->isFramebufferImageCachedForTesting(mBuffer->getId()));
         }
     }
 
@@ -519,11 +503,17 @@
     void initializeRenderEngine();
 
     std::unique_ptr<renderengine::RenderEngine> mRE;
-    std::shared_ptr<renderengine::ExternalTexture> mBuffer;
     // GLESRenderEngine for testing GLES-specific behavior.
     // Owened by mRE, but this is downcasted.
     renderengine::gl::GLESRenderEngine* mGLESRE = nullptr;
 
+    // Dumb hack to avoid NPE in the EGL driver: the GraphicBuffer needs to
+    // be freed *after* RenderEngine is destroyed, so that the EGL image is
+    // destroyed first.
+    sp<GraphicBuffer> mCurrentBuffer;
+
+    sp<GraphicBuffer> mBuffer;
+
     std::vector<uint32_t> mTexNames;
 };
 
@@ -540,7 +530,6 @@
     } else {
         mRE = renderEngineFactory->createRenderEngine();
     }
-    mBuffer = allocateDefaultBuffer();
 }
 
 struct ColorSourceVariant {
@@ -577,18 +566,18 @@
 struct BufferSourceVariant {
     static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b,
                           RenderEngineTest* fixture) {
-        const auto buf = fixture->allocateSourceBuffer(1, 1);
+        sp<GraphicBuffer> buf = RenderEngineTest::allocateSourceBuffer(1, 1);
         uint32_t texName;
         fixture->mRE->genTextures(1, &texName);
         fixture->mTexNames.push_back(texName);
 
         uint8_t* pixels;
-        buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
-                               reinterpret_cast<void**>(&pixels));
+        buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+                  reinterpret_cast<void**>(&pixels));
 
-        for (int32_t j = 0; j < buf->getBuffer()->getHeight(); j++) {
-            uint8_t* iter = pixels + (buf->getBuffer()->getStride() * j) * 4;
-            for (int32_t i = 0; i < buf->getBuffer()->getWidth(); i++) {
+        for (int32_t j = 0; j < buf->getHeight(); j++) {
+            uint8_t* iter = pixels + (buf->getStride() * j) * 4;
+            for (int32_t i = 0; i < buf->getWidth(); i++) {
                 iter[0] = uint8_t(r * 255);
                 iter[1] = uint8_t(g * 255);
                 iter[2] = uint8_t(b * 255);
@@ -597,7 +586,7 @@
             }
         }
 
-        buf->getBuffer()->unlock();
+        buf->unlock();
 
         layer.source.buffer.buffer = buf;
         layer.source.buffer.textureName = texName;
@@ -1023,14 +1012,14 @@
     layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     // Here will allocate a checker board texture, but transform texture
     // coordinates so that only the upper left is applied.
-    const auto buf = allocateSourceBuffer(2, 2);
+    sp<GraphicBuffer> buf = allocateSourceBuffer(2, 2);
     uint32_t texName;
     RenderEngineTest::mRE->genTextures(1, &texName);
     this->mTexNames.push_back(texName);
 
     uint8_t* pixels;
-    buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
-                           reinterpret_cast<void**>(&pixels));
+    buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+              reinterpret_cast<void**>(&pixels));
     // Red top left, Green top right, Blue bottom left, Black bottom right
     pixels[0] = 255;
     pixels[1] = 0;
@@ -1044,7 +1033,7 @@
     pixels[9] = 0;
     pixels[10] = 255;
     pixels[11] = 255;
-    buf->getBuffer()->unlock();
+    buf->unlock();
 
     layer.source.buffer.buffer = buf;
     layer.source.buffer.textureName = texName;
@@ -1072,19 +1061,19 @@
     std::vector<const renderengine::LayerSettings*> layers;
 
     renderengine::LayerSettings layer;
-    const auto buf = allocateSourceBuffer(1, 1);
+    sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
     uint32_t texName;
     RenderEngineTest::mRE->genTextures(1, &texName);
     this->mTexNames.push_back(texName);
 
     uint8_t* pixels;
-    buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
-                           reinterpret_cast<void**>(&pixels));
+    buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+              reinterpret_cast<void**>(&pixels));
     pixels[0] = 255;
     pixels[1] = 0;
     pixels[2] = 0;
     pixels[3] = 255;
-    buf->getBuffer()->unlock();
+    buf->unlock();
 
     layer.source.buffer.buffer = buf;
     layer.source.buffer.textureName = texName;
@@ -1111,19 +1100,19 @@
     std::vector<const renderengine::LayerSettings*> layers;
 
     renderengine::LayerSettings layer;
-    const auto buf = allocateSourceBuffer(1, 1);
+    sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
     uint32_t texName;
     RenderEngineTest::mRE->genTextures(1, &texName);
     this->mTexNames.push_back(texName);
 
     uint8_t* pixels;
-    buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
-                           reinterpret_cast<void**>(&pixels));
+    buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+              reinterpret_cast<void**>(&pixels));
     pixels[0] = 255;
     pixels[1] = 0;
     pixels[2] = 0;
     pixels[3] = 255;
-    buf->getBuffer()->unlock();
+    buf->unlock();
 
     layer.source.buffer.buffer = buf;
     layer.source.buffer.textureName = texName;
@@ -1244,7 +1233,8 @@
 }
 
 TEST_P(RenderEngineTest, drawLayers_withoutBuffers_withColorTransform) {
-    initializeRenderEngine();
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
 
     renderengine::DisplaySettings settings;
     settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -1305,6 +1295,7 @@
     layers.push_back(&layer);
 
     status_t status = mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), nullptr);
+    mCurrentBuffer = mBuffer;
     ASSERT_EQ(NO_ERROR, status);
     expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
 }
@@ -1332,8 +1323,9 @@
     layers.push_back(&layer);
 
     status_t status = mRE->drawLayers(settings, layers, mBuffer, false, base::unique_fd(), nullptr);
+    mCurrentBuffer = mBuffer;
     ASSERT_EQ(NO_ERROR, status);
-    ASSERT_FALSE(mGLESRE->isFramebufferImageCachedForTesting(mBuffer->getBuffer()->getId()));
+    ASSERT_FALSE(mGLESRE->isFramebufferImageCachedForTesting(mBuffer->getId()));
     expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
 }
 
@@ -1582,6 +1574,98 @@
     clearRegion();
 }
 
+TEST_P(RenderEngineTest, drawLayers_fillsBufferAndCachesImages) {
+    const auto& renderEngineFactory = GetParam();
+
+    if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+        // GLES-specific test
+        return;
+    }
+
+    initializeRenderEngine();
+
+    renderengine::DisplaySettings settings;
+    settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+    settings.physicalDisplay = fullscreenRect();
+    settings.clip = fullscreenRect();
+
+    std::vector<const renderengine::LayerSettings*> layers;
+
+    renderengine::LayerSettings layer;
+    layer.geometry.boundaries = fullscreenRect().toFloatRect();
+    BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
+
+    layers.push_back(&layer);
+    invokeDraw(settings, layers);
+    uint64_t bufferId = layer.source.buffer.buffer->getId();
+    EXPECT_TRUE(mGLESRE->isImageCachedForTesting(bufferId));
+    std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier =
+            mGLESRE->unbindExternalTextureBufferForTesting(bufferId);
+    std::lock_guard<std::mutex> lock(barrier->mutex);
+    ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5),
+                                            [&]() REQUIRES(barrier->mutex) {
+                                                return barrier->isOpen;
+                                            }));
+    EXPECT_FALSE(mGLESRE->isImageCachedForTesting(bufferId));
+    EXPECT_EQ(NO_ERROR, barrier->result);
+}
+
+TEST_P(RenderEngineTest, cacheExternalBuffer_withNullBuffer) {
+    const auto& renderEngineFactory = GetParam();
+
+    if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+        // GLES-specific test
+        return;
+    }
+
+    initializeRenderEngine();
+
+    std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier =
+            mGLESRE->cacheExternalTextureBufferForTesting(nullptr);
+    std::lock_guard<std::mutex> lock(barrier->mutex);
+    ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5),
+                                            [&]() REQUIRES(barrier->mutex) {
+                                                return barrier->isOpen;
+                                            }));
+    EXPECT_TRUE(barrier->isOpen);
+    EXPECT_EQ(BAD_VALUE, barrier->result);
+}
+
+TEST_P(RenderEngineTest, cacheExternalBuffer_cachesImages) {
+    const auto& renderEngineFactory = GetParam();
+
+    if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+        // GLES-specific test
+        return;
+    }
+
+    initializeRenderEngine();
+
+    sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
+    uint64_t bufferId = buf->getId();
+    std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier =
+            mGLESRE->cacheExternalTextureBufferForTesting(buf);
+    {
+        std::lock_guard<std::mutex> lock(barrier->mutex);
+        ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5),
+                                                [&]() REQUIRES(barrier->mutex) {
+                                                    return barrier->isOpen;
+                                                }));
+        EXPECT_EQ(NO_ERROR, barrier->result);
+    }
+    EXPECT_TRUE(mGLESRE->isImageCachedForTesting(bufferId));
+    barrier = mGLESRE->unbindExternalTextureBufferForTesting(bufferId);
+    {
+        std::lock_guard<std::mutex> lock(barrier->mutex);
+        ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5),
+                                                [&]() REQUIRES(barrier->mutex) {
+                                                    return barrier->isOpen;
+                                                }));
+        EXPECT_EQ(NO_ERROR, barrier->result);
+    }
+    EXPECT_FALSE(mGLESRE->isImageCachedForTesting(bufferId));
+}
+
 TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) {
     initializeRenderEngine();
 
@@ -1774,7 +1858,7 @@
         sync_wait(fd, -1);
     }
 
-    uint64_t bufferId = layer.source.buffer.buffer->getBuffer()->getId();
+    uint64_t bufferId = layer.source.buffer.buffer->getId();
     uint32_t texName = layer.source.buffer.textureName;
     EXPECT_TRUE(mGLESRE->isImageCachedForTesting(bufferId));
     EXPECT_EQ(bufferId, mGLESRE->getBufferIdForTextureNameForTesting(texName));
@@ -1882,16 +1966,16 @@
 
     // The next layer will overwrite redLayer with a GraphicBuffer that is green
     // applied with a translucent alpha.
-    const auto buf = allocateSourceBuffer(1, 1);
+    auto buf = allocateSourceBuffer(1, 1);
     {
         uint8_t* pixels;
-        buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
-                               reinterpret_cast<void**>(&pixels));
+        buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+                  reinterpret_cast<void**>(&pixels));
         pixels[0] = 0;
         pixels[1] = 255;
         pixels[2] = 0;
         pixels[3] = 255;
-        buf->getBuffer()->unlock();
+        buf->unlock();
     }
 
     const renderengine::LayerSettings greenLayer{
diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
index e3917cc..b093e88 100644
--- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp
+++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
@@ -162,18 +162,15 @@
 TEST_F(RenderEngineThreadedTest, drawLayers) {
     renderengine::DisplaySettings settings;
     std::vector<const renderengine::LayerSettings*> layers;
-    std::shared_ptr<renderengine::ExternalTexture> buffer = std::make_shared<
-            renderengine::ExternalTexture>(new GraphicBuffer(), *mRenderEngine,
-                                           renderengine::ExternalTexture::Usage::READABLE |
-                                                   renderengine::ExternalTexture::Usage::WRITEABLE);
+    sp<GraphicBuffer> buffer = new GraphicBuffer();
     base::unique_fd bufferFence;
     base::unique_fd drawFence;
 
     EXPECT_CALL(*mRenderEngine, drawLayers)
             .WillOnce([](const renderengine::DisplaySettings&,
                          const std::vector<const renderengine::LayerSettings*>&,
-                         const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                         base::unique_fd&&, base::unique_fd*) -> status_t { return NO_ERROR; });
+                         const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
+                         base::unique_fd*) -> status_t { return NO_ERROR; });
 
     status_t result = mThreadedRE->drawLayers(settings, layers, buffer, false,
                                               std::move(bufferFence), &drawFence);
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index c9f6296..194c7da 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -157,28 +157,27 @@
     resultFuture.wait();
 }
 
-void RenderEngineThreaded::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer,
-                                                    bool isRenderable) {
+void RenderEngineThreaded::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
     // This function is designed so it can run asynchronously, so we do not need to wait
     // for the futures.
     {
         std::lock_guard lock(mThreadMutex);
         mFunctionCalls.push([=](renderengine::RenderEngine& instance) {
-            ATRACE_NAME("REThreaded::mapExternalTextureBuffer");
-            instance.mapExternalTextureBuffer(buffer, isRenderable);
+            ATRACE_NAME("REThreaded::cacheExternalTextureBuffer");
+            instance.cacheExternalTextureBuffer(buffer);
         });
     }
     mCondition.notify_one();
 }
 
-void RenderEngineThreaded::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
+void RenderEngineThreaded::unbindExternalTextureBuffer(uint64_t bufferId) {
     // This function is designed so it can run asynchronously, so we do not need to wait
     // for the futures.
     {
         std::lock_guard lock(mThreadMutex);
         mFunctionCalls.push([=](renderengine::RenderEngine& instance) {
-            ATRACE_NAME("REThreaded::unmapExternalTextureBuffer");
-            instance.unmapExternalTextureBuffer(buffer);
+            ATRACE_NAME("REThreaded::unbindExternalTextureBuffer");
+            instance.unbindExternalTextureBuffer(bufferId);
         });
     }
     mCondition.notify_one();
@@ -240,7 +239,7 @@
 
 status_t RenderEngineThreaded::drawLayers(const DisplaySettings& display,
                                           const std::vector<const LayerSettings*>& layers,
-                                          const std::shared_ptr<ExternalTexture>& buffer,
+                                          const sp<GraphicBuffer>& buffer,
                                           const bool useFramebufferCache,
                                           base::unique_fd&& bufferFence,
                                           base::unique_fd* drawFence) {
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index eb6098e..61ae9b8 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -48,6 +48,8 @@
 
     void genTextures(size_t count, uint32_t* names) override;
     void deleteTextures(size_t count, uint32_t const* names) override;
+    void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
+    void unbindExternalTextureBuffer(uint64_t bufferId) override;
     size_t getMaxTextureSize() const override;
     size_t getMaxViewportDims() const override;
 
@@ -58,19 +60,14 @@
 
     status_t drawLayers(const DisplaySettings& display,
                         const std::vector<const LayerSettings*>& layers,
-                        const std::shared_ptr<ExternalTexture>& buffer,
-                        const bool useFramebufferCache, base::unique_fd&& bufferFence,
-                        base::unique_fd* drawFence) override;
+                        const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
+                        base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
 
     void cleanFramebufferCache() override;
     int getContextPriority() override;
     bool supportsBackgroundBlur() override;
     void onPrimaryDisplaySizeChanged(ui::Size size) override;
 
-protected:
-    void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) override;
-    void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
-
 private:
     void threadMain(CreateInstanceFactory factory);
     void waitUntilInitialized() const;