Add ExternalTexture class into RenderEngine interface

ExternalTexture is an RAII structure that wraps raw GraphicBuffers that
are passed into RenderEngine. ExternalTexture's role is to help with
managing GPU resources of GraphicBuffers by mapping buffers into
textures, EGLImages, or AutoBackendTextures depending on the
RenderEngine backend. Under the hood, mapExternalTextureBuffer and
unmapExternalTextureBuffer (renamed from cacheExternalTextureBuffer and
unbindExternalTextureBuffer respectively) are used to help tie
resource management to the ExternalTexture lifetime.

The main motivation for this is that currently managing buffer
lifecycle has historically been errorprone and caused memory leaks, so
this improves code health.

As part of this:
* mapExternalTextureBuffer and unmapExternalTextureBuffer
are now protected methods, and are never called outside of RenderEngine
with the exception of creating and destroying ExternalTextures.
* Because GLESRenderEngine's output buffers are cached differently from
Skia RenderEngine, if there are output-only buffers then disable the
mapExternalTextureBuffer calls whenever GLESRenderEngine is used.
* Custom RAII classes in the Planner and in BufferLayerConsumer are now
removed since they're subsumed by ExternalTexture
* RenderSurface now controls its own management of ExternalTextures in a
small queue
* cleanFramebufferCache is now unimplemented for Skia, because
ExternalTextures are now deleted whenever a RenderSurface is deleted.

Bug: 180767535
Test: libsurfaceflinger_unittest
Test: libcompositionengine_test
Test: librenderengine_test
Test: Simulate virtual displays
Test: Screen reotation
Test: Movie playback on Google TV
Test: Force GPU composition
Test: screenshot

Change-Id: I222c71e6e1c67485cdeac49e2cb829289af9efec
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index be9bce0..13ac7c3 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -188,7 +188,7 @@
     const bool blackOutLayer = (isProtected() && !targetSettings.supportsProtectedContent) ||
             (isSecure() && !targetSettings.isSecure);
     const bool bufferCanBeUsedAsHwTexture =
-            mBufferInfo.mBuffer->getUsage() & GraphicBuffer::USAGE_HW_TEXTURE;
+            mBufferInfo.mBuffer->getBuffer()->getUsage() & GraphicBuffer::USAGE_HW_TEXTURE;
     compositionengine::LayerFE::LayerSettings& layer = *result;
     if (blackOutLayer || !bufferCanBeUsedAsHwTexture) {
         ALOGE_IF(!bufferCanBeUsedAsHwTexture, "%s is blacked out as buffer is not gpu readable",
@@ -213,7 +213,7 @@
             ? mBufferInfo.mHdrMetadata.cta8613.maxContentLightLevel
             : defaultMaxContentLuminance;
     layer.frameNumber = mCurrentFrameNumber;
-    layer.bufferId = mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getId() : 0;
+    layer.bufferId = mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getBuffer()->getId() : 0;
 
     const bool useFiltering =
             targetSettings.needsFiltering || mNeedsFiltering || bufferNeedsFiltering();
@@ -314,7 +314,7 @@
                 : Hwc2::IComposerClient::Composition::DEVICE;
     }
 
-    compositionState->buffer = mBufferInfo.mBuffer;
+    compositionState->buffer = mBufferInfo.mBuffer->getBuffer();
     compositionState->bufferSlot = (mBufferInfo.mBufferSlot == BufferQueue::INVALID_BUFFER_SLOT)
             ? 0
             : mBufferInfo.mBufferSlot;
@@ -442,7 +442,7 @@
 
 void BufferLayer::gatherBufferInfo() {
     mBufferInfo.mPixelFormat =
-            !mBufferInfo.mBuffer ? PIXEL_FORMAT_NONE : mBufferInfo.mBuffer->format;
+            !mBufferInfo.mBuffer ? PIXEL_FORMAT_NONE : mBufferInfo.mBuffer->getBuffer()->format;
     mBufferInfo.mFrameLatencyNeeded = true;
 }
 
@@ -544,10 +544,10 @@
     }
 
     if (oldBufferInfo.mBuffer != nullptr) {
-        uint32_t bufWidth = mBufferInfo.mBuffer->getWidth();
-        uint32_t bufHeight = mBufferInfo.mBuffer->getHeight();
-        if (bufWidth != uint32_t(oldBufferInfo.mBuffer->width) ||
-            bufHeight != uint32_t(oldBufferInfo.mBuffer->height)) {
+        uint32_t bufWidth = mBufferInfo.mBuffer->getBuffer()->getWidth();
+        uint32_t bufHeight = mBufferInfo.mBuffer->getBuffer()->getHeight();
+        if (bufWidth != uint32_t(oldBufferInfo.mBuffer->getBuffer()->width) ||
+            bufHeight != uint32_t(oldBufferInfo.mBuffer->getBuffer()->height)) {
             recomputeVisibleRegions = true;
         }
     }
@@ -612,8 +612,8 @@
 }
 
 bool BufferLayer::isProtected() const {
-    const sp<GraphicBuffer>& buffer(mBufferInfo.mBuffer);
-    return (buffer != 0) && (buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
+    return (mBufferInfo.mBuffer != nullptr) &&
+            (mBufferInfo.mBuffer->getBuffer()->getUsage() & GRALLOC_USAGE_PROTECTED);
 }
 
 // h/w composer set-up
@@ -727,8 +727,8 @@
         return Rect::INVALID_RECT;
     }
 
-    uint32_t bufWidth = mBufferInfo.mBuffer->getWidth();
-    uint32_t bufHeight = mBufferInfo.mBuffer->getHeight();
+    uint32_t bufWidth = mBufferInfo.mBuffer->getBuffer()->getWidth();
+    uint32_t bufHeight = mBufferInfo.mBuffer->getBuffer()->getHeight();
 
     // Undo any transformations on the buffer and return the result.
     if (mBufferInfo.mTransform & ui::Transform::ROT_90) {
@@ -759,8 +759,8 @@
         return parentBounds;
     }
 
-    uint32_t bufWidth = mBufferInfo.mBuffer->getWidth();
-    uint32_t bufHeight = mBufferInfo.mBuffer->getHeight();
+    uint32_t bufWidth = mBufferInfo.mBuffer->getBuffer()->getWidth();
+    uint32_t bufHeight = mBufferInfo.mBuffer->getBuffer()->getHeight();
 
     // Undo any transformations on the buffer and return the result.
     if (mBufferInfo.mTransform & ui::Transform::ROT_90) {
@@ -802,7 +802,7 @@
         return mBufferInfo.mCrop;
     } else if (mBufferInfo.mBuffer != nullptr) {
         // otherwise we use the whole buffer
-        return mBufferInfo.mBuffer->getBounds();
+        return mBufferInfo.mBuffer->getBuffer()->getBounds();
     } else {
         // if we don't have a buffer yet, we use an empty/invalid crop
         return Rect();
@@ -847,12 +847,14 @@
 }
 
 sp<GraphicBuffer> BufferLayer::getBuffer() const {
-    return mBufferInfo.mBuffer;
+    return mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getBuffer() : nullptr;
 }
 
 void BufferLayer::getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]) {
-    GLConsumer::computeTransformMatrix(outMatrix, mBufferInfo.mBuffer, mBufferInfo.mCrop,
-                                       mBufferInfo.mTransform, filteringEnabled);
+    GLConsumer::computeTransformMatrix(outMatrix,
+                                       mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getBuffer()
+                                                           : nullptr,
+                                       mBufferInfo.mCrop, mBufferInfo.mTransform, filteringEnabled);
 }
 
 void BufferLayer::setInitialValuesForClone(const sp<Layer>& clonedFrom) {
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index b8d3f12..8553247 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -134,7 +134,7 @@
         PixelFormat mPixelFormat{PIXEL_FORMAT_NONE};
         bool mTransformToDisplayInverse{false};
 
-        sp<GraphicBuffer> mBuffer;
+        std::shared_ptr<renderengine::ExternalTexture> mBuffer;
         int mBufferSlot{BufferQueue::INVALID_BUFFER_SLOT};
 
         bool mFrameLatencyNeeded{false};
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index 69d2d11..96b2247 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -40,7 +40,6 @@
 #include <gui/ISurfaceComposer.h>
 #include <gui/SurfaceComposerClient.h>
 #include <private/gui/ComposerService.h>
-#include <renderengine/Image.h>
 #include <renderengine/RenderEngine.h>
 #include <utils/Log.h>
 #include <utils/String8.h>
@@ -167,7 +166,7 @@
     }
 
     auto buffer = mPendingRelease.isPending ? mPendingRelease.graphicBuffer
-                                            : mCurrentTextureBuffer->graphicBuffer();
+                                            : mCurrentTextureBuffer->getBuffer();
     auto err = addReleaseFence(slot, buffer, fence);
     if (err != OK) {
         BLC_LOGE("setReleaseFence: failed to add the fence: %s (%d)", strerror(-err), err);
@@ -206,9 +205,11 @@
     // before, so we need to clean up old references.
     if (item->mGraphicBuffer != nullptr) {
         std::lock_guard<std::mutex> lock(mImagesMutex);
-        if (mImages[item->mSlot] == nullptr || mImages[item->mSlot]->graphicBuffer() == nullptr ||
-            mImages[item->mSlot]->graphicBuffer()->getId() != item->mGraphicBuffer->getId()) {
-            mImages[item->mSlot] = std::make_shared<Image>(item->mGraphicBuffer, mRE);
+        if (mImages[item->mSlot] == nullptr || mImages[item->mSlot]->getBuffer() == nullptr ||
+            mImages[item->mSlot]->getBuffer()->getId() != item->mGraphicBuffer->getId()) {
+            mImages[item->mSlot] = std::make_shared<
+                    renderengine::ExternalTexture>(item->mGraphicBuffer, mRE,
+                                                   renderengine::ExternalTexture::Usage::READABLE);
         }
     }
 
@@ -222,8 +223,8 @@
     int slot = item.mSlot;
 
     BLC_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture,
-             (mCurrentTextureBuffer != nullptr && mCurrentTextureBuffer->graphicBuffer() != nullptr)
-                     ? mCurrentTextureBuffer->graphicBuffer()->handle
+             (mCurrentTextureBuffer != nullptr && mCurrentTextureBuffer->getBuffer() != nullptr)
+                     ? mCurrentTextureBuffer->getBuffer()->handle
                      : 0,
              slot, mSlots[slot].mGraphicBuffer->handle);
 
@@ -231,7 +232,7 @@
     // releaseBufferLocked() if we're in shared buffer mode and both buffers are
     // the same.
 
-    std::shared_ptr<Image> nextTextureBuffer;
+    std::shared_ptr<renderengine::ExternalTexture> nextTextureBuffer;
     {
         std::lock_guard<std::mutex> lock(mImagesMutex);
         nextTextureBuffer = mImages[slot];
@@ -241,7 +242,7 @@
     if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
         if (pendingRelease == nullptr) {
             status_t status =
-                    releaseBufferLocked(mCurrentTexture, mCurrentTextureBuffer->graphicBuffer());
+                    releaseBufferLocked(mCurrentTexture, mCurrentTextureBuffer->getBuffer());
             if (status < NO_ERROR) {
                 BLC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status),
                          status);
@@ -250,7 +251,7 @@
             }
         } else {
             pendingRelease->currentTexture = mCurrentTexture;
-            pendingRelease->graphicBuffer = mCurrentTextureBuffer->graphicBuffer();
+            pendingRelease->graphicBuffer = mCurrentTextureBuffer->getBuffer();
             pendingRelease->isPending = true;
         }
     }
@@ -301,14 +302,14 @@
 
 void BufferLayerConsumer::computeCurrentTransformMatrixLocked() {
     BLC_LOGV("computeCurrentTransformMatrixLocked");
-    if (mCurrentTextureBuffer == nullptr || mCurrentTextureBuffer->graphicBuffer() == nullptr) {
+    if (mCurrentTextureBuffer == nullptr || mCurrentTextureBuffer->getBuffer() == nullptr) {
         BLC_LOGD("computeCurrentTransformMatrixLocked: "
                  "mCurrentTextureBuffer is nullptr");
     }
     GLConsumer::computeTransformMatrix(mCurrentTransformMatrix,
                                        mCurrentTextureBuffer == nullptr
                                                ? nullptr
-                                               : mCurrentTextureBuffer->graphicBuffer(),
+                                               : mCurrentTextureBuffer->getBuffer(),
                                        getCurrentCropLocked(), mCurrentTransform,
                                        mFilteringEnabled);
 }
@@ -360,7 +361,8 @@
     return mCurrentApi;
 }
 
-sp<GraphicBuffer> BufferLayerConsumer::getCurrentBuffer(int* outSlot, sp<Fence>* outFence) const {
+std::shared_ptr<renderengine::ExternalTexture> BufferLayerConsumer::getCurrentBuffer(
+        int* outSlot, sp<Fence>* outFence) const {
     Mutex::Autolock lock(mMutex);
 
     if (outSlot != nullptr) {
@@ -371,7 +373,7 @@
         *outFence = mCurrentFence;
     }
 
-    return mCurrentTextureBuffer == nullptr ? nullptr : mCurrentTextureBuffer->graphicBuffer();
+    return mCurrentTextureBuffer == nullptr ? nullptr : mCurrentTextureBuffer;
 }
 
 Rect BufferLayerConsumer::getCurrentCrop() const {
@@ -456,10 +458,12 @@
 void BufferLayerConsumer::onBufferAvailable(const BufferItem& item) {
     if (item.mGraphicBuffer != nullptr && item.mSlot != BufferQueue::INVALID_BUFFER_SLOT) {
         std::lock_guard<std::mutex> lock(mImagesMutex);
-        const std::shared_ptr<Image>& oldImage = mImages[item.mSlot];
-        if (oldImage == nullptr || oldImage->graphicBuffer() == nullptr ||
-            oldImage->graphicBuffer()->getId() != item.mGraphicBuffer->getId()) {
-            mImages[item.mSlot] = std::make_shared<Image>(item.mGraphicBuffer, mRE);
+        const std::shared_ptr<renderengine::ExternalTexture>& oldImage = mImages[item.mSlot];
+        if (oldImage == nullptr || oldImage->getBuffer() == nullptr ||
+            oldImage->getBuffer()->getId() != item.mGraphicBuffer->getId()) {
+            mImages[item.mSlot] = std::make_shared<
+                    renderengine::ExternalTexture>(item.mGraphicBuffer, mRE,
+                                                   renderengine::ExternalTexture::Usage::READABLE);
         }
     }
 }
@@ -499,22 +503,6 @@
 
     ConsumerBase::dumpLocked(result, prefix);
 }
-
-BufferLayerConsumer::Image::Image(const sp<GraphicBuffer>& graphicBuffer,
-                                  renderengine::RenderEngine& engine)
-      : mGraphicBuffer(graphicBuffer), mRE(engine) {
-    if (graphicBuffer != nullptr && (graphicBuffer->getUsage() & GRALLOC_USAGE_PROTECTED)) {
-        return;
-    }
-    mRE.cacheExternalTextureBuffer(mGraphicBuffer);
-}
-
-BufferLayerConsumer::Image::~Image() {
-    if (mGraphicBuffer != nullptr) {
-        ALOGV("Destroying buffer: %" PRId64, mGraphicBuffer->getId());
-        mRE.unbindExternalTextureBuffer(mGraphicBuffer->getId());
-    }
-}
 }; // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h
index dd39214..9ed80b4 100644
--- a/services/surfaceflinger/BufferLayerConsumer.h
+++ b/services/surfaceflinger/BufferLayerConsumer.h
@@ -21,12 +21,11 @@
 #include <gui/BufferQueueDefs.h>
 #include <gui/ConsumerBase.h>
 #include <gui/HdrMetadata.h>
-
+#include <renderengine/ExternalTexture.h>
 #include <ui/FenceTime.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/GraphicTypes.h>
 #include <ui/Region.h>
-
 #include <utils/String8.h>
 #include <utils/Vector.h>
 #include <utils/threads.h>
@@ -39,7 +38,6 @@
 
 namespace renderengine {
 class RenderEngine;
-class Image;
 } // namespace renderengine
 
 /*
@@ -153,7 +151,8 @@
     // 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;
+    std::shared_ptr<renderengine::ExternalTexture> getCurrentBuffer(
+            int* outSlot = nullptr, sp<Fence>* outFence = nullptr) const;
 
     // getCurrentCrop returns the cropping rectangle of the current buffer.
     Rect getCurrentCrop() const;
@@ -258,7 +257,7 @@
     // mCurrentTextureBuffer is the buffer containing the current texture. It's
     // possible that this buffer is not associated with any buffer slot, so we
     // must track it separately in order to support the getCurrentBuffer method.
-    std::shared_ptr<Image> mCurrentTextureBuffer;
+    std::shared_ptr<renderengine::ExternalTexture> mCurrentTextureBuffer;
 
     // mCurrentCrop is the crop rectangle that applies to the current texture.
     // It gets set each time updateTexImage is called.
@@ -337,7 +336,8 @@
     int mCurrentTexture;
 
     // Shadow buffer cache for cleaning up renderengine references.
-    std::shared_ptr<Image> mImages[BufferQueueDefs::NUM_BUFFER_SLOTS] GUARDED_BY(mImagesMutex);
+    std::shared_ptr<renderengine::ExternalTexture>
+            mImages[BufferQueueDefs::NUM_BUFFER_SLOTS] GUARDED_BY(mImagesMutex);
 
     // Separate mutex guarding the shadow buffer cache.
     // mImagesMutex can be manipulated with binder threads (e.g. onBuffersAllocated)
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index ed826a0..c533969 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -71,14 +71,8 @@
     // original layer and the clone should be removed at the same time so there shouldn't be any
     // issue with the clone layer trying to use the texture.
     if (mBufferInfo.mBuffer != nullptr && !isClone()) {
-        // Ensure that mBuffer is uncached from RenderEngine here, as
-        // RenderEngine may have been using the buffer as an external texture
-        // after the client uncached the buffer.
-        auto& engine(mFlinger->getRenderEngine());
-        const uint64_t bufferId = mBufferInfo.mBuffer->getId();
-        engine.unbindExternalTextureBuffer(bufferId);
-        callReleaseBufferCallback(mDrawingState.releaseBufferListener, mBufferInfo.mBuffer,
-                                  mBufferInfo.mFence);
+        callReleaseBufferCallback(mDrawingState.releaseBufferListener,
+                                  mBufferInfo.mBuffer->getBuffer(), mBufferInfo.mFence);
     }
 }
 
@@ -363,8 +357,9 @@
     return true;
 }
 
-bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence,
-                                 nsecs_t postTime, nsecs_t desiredPresentTime, bool isAutoTimestamp,
+bool BufferStateLayer::setBuffer(const std::shared_ptr<renderengine::ExternalTexture>& buffer,
+                                 const sp<Fence>& acquireFence, nsecs_t postTime,
+                                 nsecs_t desiredPresentTime, bool isAutoTimestamp,
                                  const client_cache_t& clientCacheId, uint64_t frameNumber,
                                  std::optional<nsecs_t> dequeueTime, const FrameTimelineInfo& info,
                                  const sp<ITransactionCompletedListener>& releaseBufferListener) {
@@ -372,12 +367,14 @@
 
     if (mCurrentState.buffer) {
         mReleasePreviousBuffer = true;
-        if (mCurrentState.buffer != mDrawingState.buffer) {
+        if (!mDrawingState.buffer ||
+            mCurrentState.buffer->getBuffer() != mDrawingState.buffer->getBuffer()) {
             // If mCurrentState has a buffer, and we are about to update again
             // before swapping to drawing state, then the first buffer will be
             // dropped and we should decrement the pending buffer count and
             // call any release buffer callbacks if set.
-            callReleaseBufferCallback(mCurrentState.releaseBufferListener, mCurrentState.buffer,
+            callReleaseBufferCallback(mCurrentState.releaseBufferListener,
+                                      mCurrentState.buffer->getBuffer(),
                                       mCurrentState.acquireFence);
             decrementPendingBufferCount();
             if (mCurrentState.bufferSurfaceFrameTX != nullptr) {
@@ -415,8 +412,8 @@
 
     setFrameTimelineVsyncForBufferTransaction(info, postTime);
 
-    if (dequeueTime && *dequeueTime != 0) {
-        const uint64_t bufferId = buffer->getId();
+    if (buffer && dequeueTime && *dequeueTime != 0) {
+        const uint64_t bufferId = buffer->getBuffer()->getId();
         mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str());
         mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, *dequeueTime,
                                                FrameTracer::FrameEvent::DEQUEUE);
@@ -424,8 +421,8 @@
                                                FrameTracer::FrameEvent::QUEUE);
     }
 
-    mCurrentState.width = mCurrentState.buffer->width;
-    mCurrentState.height = mCurrentState.buffer->height;
+    mCurrentState.width = mCurrentState.buffer->getBuffer()->getWidth();
+    mCurrentState.height = mCurrentState.buffer->getBuffer()->getHeight();
 
     return true;
 }
@@ -674,7 +671,7 @@
     }
 
     const int32_t layerId = getSequence();
-    const uint64_t bufferId = mDrawingState.buffer->getId();
+    const uint64_t bufferId = mDrawingState.buffer->getBuffer()->getId();
     const uint64_t frameNumber = mDrawingState.frameNumber;
     const auto acquireFence = std::make_shared<FenceTime>(mDrawingState.acquireFence);
     mFlinger->mTimeStats->setAcquireFence(layerId, frameNumber, acquireFence);
@@ -708,7 +705,7 @@
         return BAD_VALUE;
     }
 
-    if (s.buffer != mBufferInfo.mBuffer) {
+    if (!mBufferInfo.mBuffer || s.buffer->getBuffer() != mBufferInfo.mBuffer->getBuffer()) {
         decrementPendingBufferCount();
     }
 
@@ -827,13 +824,13 @@
 
 Rect BufferStateLayer::computeCrop(const State& s) {
     if (s.crop.isEmpty() && s.buffer) {
-        return s.buffer->getBounds();
+        return s.buffer->getBuffer()->getBounds();
     } else if (s.buffer) {
         Rect crop = s.crop;
         crop.left = std::max(crop.left, 0);
         crop.top = std::max(crop.top, 0);
-        uint32_t bufferWidth = s.buffer->getWidth();
-        uint32_t bufferHeight = s.buffer->getHeight();
+        uint32_t bufferWidth = s.buffer->getBuffer()->getWidth();
+        uint32_t bufferHeight = s.buffer->getBuffer()->getHeight();
         if (bufferHeight <= std::numeric_limits<int32_t>::max() &&
             bufferWidth <= std::numeric_limits<int32_t>::max()) {
             crop.right = std::min(crop.right, static_cast<int32_t>(bufferWidth));
@@ -841,7 +838,7 @@
         }
         if (!crop.isValid()) {
             // Crop rect is out of bounds, return whole buffer
-            return s.buffer->getBounds();
+            return s.buffer->getBuffer()->getBounds();
         }
         return crop;
     }
@@ -863,8 +860,8 @@
         return false;
     }
 
-    uint32_t bufferWidth = s.buffer->width;
-    uint32_t bufferHeight = s.buffer->height;
+    uint32_t bufferWidth = s.buffer->getBuffer()->width;
+    uint32_t bufferHeight = s.buffer->getBuffer()->height;
 
     // Undo any transformations on the buffer and return the result.
     if (s.bufferTransform & ui::Transform::ROT_90) {
@@ -891,14 +888,16 @@
     ATRACE_INT(mBlastTransactionName.c_str(), pendingBuffers);
 }
 
-void BufferStateLayer::bufferMayChange(sp<GraphicBuffer>& newBuffer) {
-    if (mDrawingState.buffer != nullptr && mDrawingState.buffer != mBufferInfo.mBuffer &&
-        newBuffer != mDrawingState.buffer) {
+void BufferStateLayer::bufferMayChange(const sp<GraphicBuffer>& newBuffer) {
+    if (mDrawingState.buffer != nullptr &&
+        (!mBufferInfo.mBuffer ||
+         mDrawingState.buffer->getBuffer() != mBufferInfo.mBuffer->getBuffer()) &&
+        newBuffer != mDrawingState.buffer->getBuffer()) {
         // If we are about to update mDrawingState.buffer but it has not yet latched
         // then we will drop a buffer and should decrement the pending buffer count and
         // call any release buffer callbacks if set.
-        callReleaseBufferCallback(mDrawingState.releaseBufferListener, mDrawingState.buffer,
-                                  mDrawingState.acquireFence);
+        callReleaseBufferCallback(mDrawingState.releaseBufferListener,
+                                  mDrawingState.buffer->getBuffer(), mDrawingState.acquireFence);
         decrementPendingBufferCount();
     }
 }
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 8ce3e1f..a69d07f 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -66,9 +66,9 @@
     bool setTransform(uint32_t transform) override;
     bool setTransformToDisplayInverse(bool transformToDisplayInverse) override;
     bool setCrop(const Rect& crop) override;
-    bool setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence, nsecs_t postTime,
-                   nsecs_t desiredPresentTime, bool isAutoTimestamp,
-                   const client_cache_t& clientCacheId, uint64_t frameNumber,
+    bool setBuffer(const std::shared_ptr<renderengine::ExternalTexture>& buffer,
+                   const sp<Fence>& acquireFence, nsecs_t postTime, nsecs_t desiredPresentTime,
+                   bool isAutoTimestamp, const client_cache_t& clientCacheId, uint64_t frameNumber,
                    std::optional<nsecs_t> dequeueTime, const FrameTimelineInfo& info,
                    const sp<ITransactionCompletedListener>& transactionListener) override;
     bool setAcquireFence(const sp<Fence>& fence) override;
@@ -108,7 +108,7 @@
 
     // See mPendingBufferTransactions
     void decrementPendingBufferCount();
-    void bufferMayChange(sp<GraphicBuffer>& newBuffer) override;
+    void bufferMayChange(const sp<GraphicBuffer>& newBuffer) override;
     std::atomic<int32_t>* getPendingBufferCounter() override { return &mPendingBufferTransactions; }
     std::string getPendingBufferCounterName() override { return mBlastTransactionName; }
 
diff --git a/services/surfaceflinger/ClientCache.cpp b/services/surfaceflinger/ClientCache.cpp
index 44b33ef..f310738 100644
--- a/services/surfaceflinger/ClientCache.cpp
+++ b/services/surfaceflinger/ClientCache.cpp
@@ -102,7 +102,12 @@
         return false;
     }
 
-    processBuffers[id].buffer = buffer;
+    LOG_ALWAYS_FATAL_IF(mRenderEngine == nullptr,
+                        "Attempted to build the ClientCache before a RenderEngine instance was "
+                        "ready!");
+    processBuffers[id].buffer = std::make_shared<
+            renderengine::ExternalTexture>(buffer, *mRenderEngine,
+                                           renderengine::ExternalTexture::Usage::READABLE);
     return true;
 }
 
@@ -132,7 +137,7 @@
     }
 }
 
-sp<GraphicBuffer> ClientCache::get(const client_cache_t& cacheId) {
+std::shared_ptr<renderengine::ExternalTexture> ClientCache::get(const client_cache_t& cacheId) {
     std::lock_guard lock(mMutex);
 
     ClientCacheBuffer* buf = nullptr;
@@ -213,8 +218,8 @@
         auto &buffers = i.second.second;
         for (auto& [id, clientCacheBuffer] : buffers) {
             StringAppendF(&result, "\t ID: %d, Width/Height: %d,%d\n", (int)id,
-                          (int)clientCacheBuffer.buffer->getWidth(),
-                          (int)clientCacheBuffer.buffer->getHeight());
+                          (int)clientCacheBuffer.buffer->getBuffer()->getWidth(),
+                          (int)clientCacheBuffer.buffer->getBuffer()->getHeight());
         }
     }
 }
diff --git a/services/surfaceflinger/ClientCache.h b/services/surfaceflinger/ClientCache.h
index 0d597c8..a9b8177 100644
--- a/services/surfaceflinger/ClientCache.h
+++ b/services/surfaceflinger/ClientCache.h
@@ -19,6 +19,7 @@
 #include <android-base/thread_annotations.h>
 #include <binder/IBinder.h>
 #include <gui/LayerState.h>
+#include <renderengine/RenderEngine.h>
 #include <ui/GraphicBuffer.h>
 #include <utils/RefBase.h>
 #include <utils/Singleton.h>
@@ -39,7 +40,11 @@
     bool add(const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer);
     void erase(const client_cache_t& cacheId);
 
-    sp<GraphicBuffer> get(const client_cache_t& cacheId);
+    std::shared_ptr<renderengine::ExternalTexture> get(const client_cache_t& cacheId);
+
+    // Always called immediately after setup. Will be set to non-null, and then should never be
+    // called again.
+    void setRenderEngine(renderengine::RenderEngine* renderEngine) { mRenderEngine = renderEngine; }
 
     void removeProcess(const wp<IBinder>& processToken);
 
@@ -59,7 +64,7 @@
     std::mutex mMutex;
 
     struct ClientCacheBuffer {
-        sp<GraphicBuffer> buffer;
+        std::shared_ptr<renderengine::ExternalTexture> buffer;
         std::set<wp<ErasedRecipient>> recipients;
     };
     std::map<wp<IBinder> /*caching process*/,
@@ -73,6 +78,7 @@
     };
 
     sp<CacheDeathRecipient> mDeathRecipient;
+    renderengine::RenderEngine* mRenderEngine = nullptr;
 
     bool getBuffer(const client_cache_t& cacheId, ClientCacheBuffer** outClientCacheBuffer)
             REQUIRES(mMutex);
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
index f680460..daee83b 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
@@ -16,15 +16,16 @@
 
 #pragma once
 
-#include <cstdint>
-#include <vector>
-
+#include <renderengine/ExternalTexture.h>
 #include <ui/Fence.h>
 #include <ui/GraphicTypes.h>
 #include <ui/Size.h>
 #include <utils/Errors.h>
 #include <utils/StrongPointer.h>
 
+#include <cstdint>
+#include <vector>
+
 namespace android {
 
 class GraphicBuffer;
@@ -80,7 +81,8 @@
     virtual void prepareFrame(bool usesClientComposition, bool usesDeviceComposition) = 0;
 
     // Allocates a buffer as scratch space for GPU composition
-    virtual sp<GraphicBuffer> dequeueBuffer(base::unique_fd* bufferFence) = 0;
+    virtual std::shared_ptr<renderengine::ExternalTexture> dequeueBuffer(
+            base::unique_fd* bufferFence) = 0;
 
     // Queues the drawn buffer for consumption by HWC. readyFence is the fence
     // which will fire when the buffer is ready for consumption.
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurfaceCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurfaceCreationArgs.h
index a1230b3..a8d372c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurfaceCreationArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurfaceCreationArgs.h
@@ -45,6 +45,8 @@
 
     // The DisplaySurface for this surface
     sp<DisplaySurface> displaySurface;
+
+    size_t maxTextureCacheSize;
 };
 
 /**
@@ -81,6 +83,11 @@
         return *this;
     }
 
+    RenderSurfaceCreationArgsBuilder& setMaxTextureCacheSize(size_t maxTextureCacheSize) {
+        mArgs.maxTextureCacheSize = maxTextureCacheSize;
+        return *this;
+    }
+
 private:
     RenderSurfaceCreationArgs mArgs;
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index 48a54d6..c61ec59 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -18,7 +18,7 @@
 
 #include <compositionengine/ProjectionSpace.h>
 #include <compositionengine/impl/HwcBufferCache.h>
-#include <renderengine/Mesh.h>
+#include <renderengine/ExternalTexture.h>
 #include <ui/FloatRect.h>
 #include <ui/GraphicTypes.h>
 #include <ui/Rect.h>
@@ -89,7 +89,7 @@
 
     // Overrides the buffer, acquire fence, and display frame stored in LayerFECompositionState
     struct {
-        sp<GraphicBuffer> buffer = nullptr;
+        std::shared_ptr<renderengine::ExternalTexture> buffer = nullptr;
         sp<Fence> acquireFence = nullptr;
         Rect displayFrame = {};
         ui::Dataspace dataspace{ui::Dataspace::UNKNOWN};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
index 5127a6f..a8a5380 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
@@ -16,12 +16,16 @@
 
 #pragma once
 
-#include <memory>
-
 #include <android-base/unique_fd.h>
 #include <compositionengine/RenderSurface.h>
 #include <utils/StrongPointer.h>
 
+#include <memory>
+#include <vector>
+
+#include "renderengine/ExternalTexture.h"
+#include "renderengine/RenderEngine.h"
+
 struct ANativeWindow;
 
 namespace android {
@@ -54,7 +58,8 @@
     void setProtected(bool useProtected) override;
     status_t beginFrame(bool mustRecompose) override;
     void prepareFrame(bool usesClientComposition, bool usesDeviceComposition) override;
-    sp<GraphicBuffer> dequeueBuffer(base::unique_fd* bufferFence) override;
+    std::shared_ptr<renderengine::ExternalTexture> dequeueBuffer(
+            base::unique_fd* bufferFence) override;
     void queueBuffer(base::unique_fd readyFence) override;
     void onPresentDisplayCompleted() override;
     void flip() override;
@@ -66,7 +71,7 @@
     // Testing
     void setPageFlipCountForTest(std::uint32_t);
     void setSizeForTest(const ui::Size&);
-    sp<GraphicBuffer>& mutableGraphicBufferForTest();
+    std::shared_ptr<renderengine::ExternalTexture>& mutableTextureForTest();
     base::unique_fd& mutableBufferReadyForTest();
 
 private:
@@ -75,10 +80,13 @@
 
     // ANativeWindow being rendered into
     const sp<ANativeWindow> mNativeWindow;
-    // Current buffer being rendered into
-    sp<GraphicBuffer> mGraphicBuffer;
+
+    std::vector<std::shared_ptr<renderengine::ExternalTexture>> mTextureCache;
+    // Current texture being rendered into
+    std::shared_ptr<renderengine::ExternalTexture> mTexture;
     const sp<DisplaySurface> mDisplaySurface;
     ui::Size mSize;
+    const size_t mMaxTextureCacheSize;
     bool mProtected{false};
     std::uint32_t mPageFlipCount{0};
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
index c5d03a7..53f4a30 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -66,7 +66,7 @@
     const Rect& getBounds() const { return mBounds; }
     const Region& getVisibleRegion() const { return mVisibleRegion; }
     size_t getAge() const { return mAge; }
-    const sp<GraphicBuffer>& getBuffer() const { return mTexture.getBuffer(); }
+    const std::shared_ptr<renderengine::ExternalTexture>& getBuffer() const { return mTexture; }
     const sp<Fence>& getDrawFence() const { return mDrawFence; }
     const ProjectionSpace& getOutputSpace() const { return mOutputSpace; }
     ui::Dataspace getOutputDataspace() const { return mOutputDataspace; }
@@ -87,7 +87,7 @@
 
     void setLastUpdate(std::chrono::steady_clock::time_point now) { mLastUpdate = now; }
     void append(const CachedSet& other) {
-        mTexture.setBuffer(nullptr, nullptr);
+        mTexture = nullptr;
         mOutputDataspace = ui::Dataspace::UNKNOWN;
         mDrawFence = nullptr;
 
@@ -115,31 +115,7 @@
     Region mVisibleRegion;
     size_t mAge = 0;
 
-    class Texture {
-    public:
-        ~Texture() { setBuffer(nullptr, nullptr); }
-
-        void setBuffer(const sp<GraphicBuffer>& buffer, renderengine::RenderEngine* re) {
-            if (mRE && mBuffer) {
-                mRE->unbindExternalTextureBuffer(mBuffer->getId());
-            }
-
-            mBuffer = buffer;
-            mRE = re;
-
-            if (mRE && mBuffer) {
-                mRE->cacheExternalTextureBuffer(mBuffer);
-            }
-        }
-
-        const sp<GraphicBuffer>& getBuffer() const { return mBuffer; }
-
-    private:
-        sp<GraphicBuffer> mBuffer = nullptr;
-        renderengine::RenderEngine* mRE = nullptr;
-    };
-
-    Texture mTexture;
+    std::shared_ptr<renderengine::ExternalTexture> mTexture;
     sp<Fence> mDrawFence;
     ProjectionSpace mOutputSpace;
     ui::Dataspace mOutputDataspace;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
index a0cae6f..fe858c2 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
@@ -39,7 +39,7 @@
     MOCK_METHOD1(setBufferPixelFormat, void(ui::PixelFormat));
     MOCK_METHOD1(beginFrame, status_t(bool mustRecompose));
     MOCK_METHOD2(prepareFrame, void(bool, bool));
-    MOCK_METHOD1(dequeueBuffer, sp<GraphicBuffer>(base::unique_fd*));
+    MOCK_METHOD1(dequeueBuffer, std::shared_ptr<renderengine::ExternalTexture>(base::unique_fd*));
     MOCK_METHOD1(queueBuffer, void(base::unique_fd));
     MOCK_METHOD0(onPresentDisplayCompleted, void());
     MOCK_METHOD0(flip, void());
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 3ac5433..3468b20 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#include <thread>
-
+#include <SurfaceFlingerProperties.sysprop.h>
 #include <android-base/stringprintf.h>
 #include <compositionengine/CompositionEngine.h>
 #include <compositionengine/CompositionRefreshArgs.h>
@@ -29,7 +28,9 @@
 #include <compositionengine/impl/OutputLayerCompositionState.h>
 #include <compositionengine/impl/planner/Planner.h>
 
-#include <SurfaceFlingerProperties.sysprop.h>
+#include <thread>
+
+#include "renderengine/ExternalTexture.h"
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
@@ -715,11 +716,11 @@
         bool skipLayer = false;
         if (layer->getState().overrideInfo.buffer != nullptr) {
             if (previousOverride != nullptr &&
-                layer->getState().overrideInfo.buffer == previousOverride) {
+                layer->getState().overrideInfo.buffer->getBuffer() == previousOverride) {
                 ALOGV("Skipping redundant buffer");
                 skipLayer = true;
             }
-            previousOverride = layer->getState().overrideInfo.buffer;
+            previousOverride = layer->getState().overrideInfo.buffer->getBuffer();
         }
 
         const bool includeGeometry = refreshArgs.updatingGeometryThisFrame;
@@ -978,14 +979,15 @@
     }
 
     base::unique_fd fd;
-    sp<GraphicBuffer> buf;
+
+    std::shared_ptr<renderengine::ExternalTexture> tex;
 
     // If we aren't doing client composition on this output, but do have a
     // flipClientTarget request for this frame on this output, we still need to
     // dequeue a buffer.
     if (hasClientComposition || outputState.flipClientTarget) {
-        buf = mRenderSurface->dequeueBuffer(&fd);
-        if (buf == nullptr) {
+        tex = mRenderSurface->dequeueBuffer(&fd);
+        if (tex == nullptr) {
             ALOGW("Dequeuing buffer for display [%s] failed, bailing out of "
                   "client composition for this frame",
                   mName.c_str());
@@ -1030,13 +1032,14 @@
     // Check if the client composition requests were rendered into the provided graphic buffer. If
     // so, we can reuse the buffer and avoid client composition.
     if (mClientCompositionRequestCache) {
-        if (mClientCompositionRequestCache->exists(buf->getId(), clientCompositionDisplay,
+        if (mClientCompositionRequestCache->exists(tex->getBuffer()->getId(),
+                                                   clientCompositionDisplay,
                                                    clientCompositionLayers)) {
             outputCompositionState.reusedClientComposition = true;
             setExpensiveRenderingExpected(false);
             return readyFence;
         }
-        mClientCompositionRequestCache->add(buf->getId(), clientCompositionDisplay,
+        mClientCompositionRequestCache->add(tex->getBuffer()->getId(), clientCompositionDisplay,
                                             clientCompositionLayers);
     }
 
@@ -1069,12 +1072,12 @@
     // over to RenderEngine, in which case this flag can be removed from the drawLayers interface.
     const bool useFramebufferCache = outputState.layerStackInternal;
     status_t status =
-            renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, buf,
+            renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, tex,
                                     useFramebufferCache, std::move(fd), &readyFence);
 
     if (status != NO_ERROR && mClientCompositionRequestCache) {
         // If rendering was not successful, remove the request from the cache.
-        mClientCompositionRequestCache->remove(buf->getId());
+        mClientCompositionRequestCache->remove(tex->getBuffer()->getId());
     }
 
     auto& timeStats = getCompositionEngine().getTimeStats();
@@ -1151,9 +1154,9 @@
 
             std::vector<LayerFE::LayerSettings> results;
             if (layer->getState().overrideInfo.buffer != nullptr) {
-                if (layer->getState().overrideInfo.buffer != previousOverrideBuffer) {
+                if (layer->getState().overrideInfo.buffer->getBuffer() != previousOverrideBuffer) {
                     results = layer->getOverrideCompositionList();
-                    previousOverrideBuffer = layer->getState().overrideInfo.buffer;
+                    previousOverrideBuffer = layer->getState().overrideInfo.buffer->getBuffer();
                     ALOGV("Replacing [%s] with override in RE", layer->getLayerFE().getDebugName());
                 } else {
                     ALOGV("Skipping redundant override buffer for [%s] in RE",
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index f640f85..9ca8914 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -537,7 +537,7 @@
     sp<GraphicBuffer> buffer = outputIndependentState.buffer;
     sp<Fence> acquireFence = outputIndependentState.acquireFence;
     if (getState().overrideInfo.buffer != nullptr) {
-        buffer = getState().overrideInfo.buffer;
+        buffer = getState().overrideInfo.buffer->getBuffer();
         acquireFence = getState().overrideInfo.acquireFence;
     }
 
@@ -699,7 +699,7 @@
     settings.geometry = renderengine::Geometry{
             .boundaries = boundaries.toFloatRect(),
     };
-    settings.bufferId = getState().overrideInfo.buffer->getId();
+    settings.bufferId = getState().overrideInfo.buffer->getBuffer()->getId();
     settings.source = renderengine::PixelSource{
             .buffer = renderengine::Buffer{
                     .buffer = getState().overrideInfo.buffer,
diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
index 3bef77d..ef50870 100644
--- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
@@ -25,8 +25,8 @@
 #include <compositionengine/impl/DumpHelpers.h>
 #include <compositionengine/impl/OutputCompositionState.h>
 #include <compositionengine/impl/RenderSurface.h>
-
 #include <log/log.h>
+#include <renderengine/ExternalTexture.h>
 #include <renderengine/RenderEngine.h>
 #include <system/window.h>
 #include <ui/GraphicBuffer.h>
@@ -63,7 +63,8 @@
         mDisplay(display),
         mNativeWindow(args.nativeWindow),
         mDisplaySurface(args.displaySurface),
-        mSize(args.displayWidth, args.displayHeight) {
+        mSize(args.displayWidth, args.displayHeight),
+        mMaxTextureCacheSize(args.maxTextureCacheSize) {
     LOG_ALWAYS_FATAL_IF(!mNativeWindow);
 }
 
@@ -146,7 +147,8 @@
     }
 }
 
-sp<GraphicBuffer> RenderSurface::dequeueBuffer(base::unique_fd* bufferFence) {
+std::shared_ptr<renderengine::ExternalTexture> RenderSurface::dequeueBuffer(
+        base::unique_fd* bufferFence) {
     ATRACE_CALL();
     int fd = -1;
     ANativeWindowBuffer* buffer = nullptr;
@@ -158,16 +160,41 @@
               mDisplay.getName().c_str(), result);
         // Return fast here as we can't do much more - any rendering we do
         // now will just be wrong.
-        return mGraphicBuffer;
+        return mTexture;
     }
 
-    ALOGW_IF(mGraphicBuffer != nullptr, "Clobbering a non-null pointer to a buffer [%p].",
-             mGraphicBuffer->getNativeBuffer()->handle);
-    mGraphicBuffer = GraphicBuffer::from(buffer);
+    ALOGW_IF(mTexture != nullptr, "Clobbering a non-null pointer to a buffer [%p].",
+             mTexture->getBuffer()->getNativeBuffer()->handle);
+
+    sp<GraphicBuffer> newBuffer = GraphicBuffer::from(buffer);
+
+    std::shared_ptr<renderengine::ExternalTexture> texture;
+
+    for (auto it = mTextureCache.begin(); it != mTextureCache.end(); it++) {
+        const auto& cachedTexture = *it;
+        if (cachedTexture->getBuffer()->getId() == newBuffer->getId()) {
+            texture = cachedTexture;
+            mTextureCache.erase(it);
+            break;
+        }
+    }
+
+    if (texture) {
+        mTexture = texture;
+    } else {
+        mTexture = std::make_shared<
+                renderengine::ExternalTexture>(GraphicBuffer::from(buffer),
+                                               mCompositionEngine.getRenderEngine(),
+                                               renderengine::ExternalTexture::Usage::WRITEABLE);
+    }
+    mTextureCache.push_back(mTexture);
+    if (mTextureCache.size() > mMaxTextureCacheSize) {
+        mTextureCache.erase(mTextureCache.begin());
+    }
 
     *bufferFence = base::unique_fd(fd);
 
-    return mGraphicBuffer;
+    return mTexture;
 }
 
 void RenderSurface::queueBuffer(base::unique_fd readyFence) {
@@ -177,24 +204,24 @@
         // hasFlipClientTargetRequest could return true even if we haven't
         // dequeued a buffer before. Try dequeueing one if we don't have a
         // buffer ready.
-        if (mGraphicBuffer == nullptr) {
+        if (mTexture == nullptr) {
             ALOGI("Attempting to queue a client composited buffer without one "
                   "previously dequeued for display [%s]. Attempting to dequeue "
                   "a scratch buffer now",
                   mDisplay.getName().c_str());
-            // We shouldn't deadlock here, since mGraphicBuffer == nullptr only
+            // We shouldn't deadlock here, since mTexture == nullptr only
             // after a successful call to queueBuffer, or if dequeueBuffer has
             // never been called.
             base::unique_fd unused;
             dequeueBuffer(&unused);
         }
 
-        if (mGraphicBuffer == nullptr) {
+        if (mTexture == 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(),
+                                                         mTexture->getBuffer()->getNativeBuffer(),
+                                                         dup(readyFence));
             if (result != NO_ERROR) {
                 ALOGE("Error when queueing buffer for display [%s]: %d", mDisplay.getName().c_str(),
                       result);
@@ -204,11 +231,12 @@
                     LOG_ALWAYS_FATAL("ANativeWindow::queueBuffer failed with error: %d", result);
                 } else {
                     mNativeWindow->cancelBuffer(mNativeWindow.get(),
-                                                mGraphicBuffer->getNativeBuffer(), dup(readyFence));
+                                                mTexture->getBuffer()->getNativeBuffer(),
+                                                dup(readyFence));
                 }
             }
 
-            mGraphicBuffer = nullptr;
+            mTexture = nullptr;
         }
     }
 
@@ -256,8 +284,8 @@
     mSize = size;
 }
 
-sp<GraphicBuffer>& RenderSurface::mutableGraphicBufferForTest() {
-    return mGraphicBuffer;
+std::shared_ptr<renderengine::ExternalTexture>& RenderSurface::mutableTextureForTest() {
+    return mTexture;
 }
 
 } // namespace impl
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index dcb7555..53557bb 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -130,7 +130,7 @@
 }
 
 bool CachedSet::hasReadyBuffer() const {
-    return mTexture.getBuffer() != nullptr && mDrawFence->getStatus() == Fence::Status::Signaled;
+    return mTexture != nullptr && mDrawFence->getStatus() == Fence::Status::Signaled;
 }
 
 std::vector<CachedSet> CachedSet::decompose() const {
@@ -217,21 +217,27 @@
     sp<GraphicBuffer> buffer = new GraphicBuffer(static_cast<uint32_t>(mBounds.getWidth()),
                                                  static_cast<uint32_t>(mBounds.getHeight()),
                                                  HAL_PIXEL_FORMAT_RGBA_8888, 1, usageFlags);
+    const auto texture = std::make_shared<
+            renderengine::ExternalTexture>(buffer, renderEngine,
+                                           renderengine::ExternalTexture::Usage::READABLE |
+                                                   renderengine::ExternalTexture::Usage::WRITEABLE);
     LOG_ALWAYS_FATAL_IF(buffer->initCheck() != OK);
     base::unique_fd drawFence;
 
-    status_t result = renderEngine.drawLayers(displaySettings, layerSettingsPointers, buffer, false,
-                                              base::unique_fd(), &drawFence);
+    status_t result = renderEngine.drawLayers(displaySettings, layerSettingsPointers, mTexture,
+                                              false, base::unique_fd(), &drawFence);
 
     if (result == NO_ERROR) {
-        mTexture.setBuffer(buffer, &renderEngine);
         mDrawFence = new Fence(drawFence.release());
         mOutputSpace = ProjectionSpace(ui::Size(outputState.framebufferSpace.bounds.getWidth(),
                                                 outputState.framebufferSpace.bounds.getHeight()),
                                        mBounds);
+        mTexture = std::move(texture);
         mOutputSpace.orientation = outputState.framebufferSpace.orientation;
         mOutputDataspace = outputDataspace;
         mOrientation = orientation;
+    } else {
+        mTexture = nullptr;
     }
 }
 
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
index ad75557..3a2534b 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -111,7 +111,12 @@
     const GraphicBuffer* currentOverrideBuffer = nullptr;
     bool hasSkippedLayers = false;
     for (auto layer : layers) {
-        const GraphicBuffer* overrideBuffer = layer->getState().overrideInfo.buffer.get();
+        if (!layer->getState().overrideInfo.buffer) {
+            continue;
+        }
+
+        const GraphicBuffer* overrideBuffer =
+                layer->getState().overrideInfo.buffer->getBuffer().get();
         if (overrideBuffer != nullptr && overrideBuffer == currentOverrideBuffer) {
             // Skip this layer since it is part of a previous cached set
             hasSkippedLayers = true;
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 8a4d161..4c3f494 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -26,6 +26,7 @@
 #include "MockHWC2.h"
 #include "MockHWComposer.h"
 #include "RegionMatcher.h"
+#include "renderengine/mock/RenderEngine.h"
 
 namespace android::compositionengine {
 namespace {
@@ -715,7 +716,7 @@
     static const HdrMetadata kHdrMetadata;
     static native_handle_t* kSidebandStreamHandle;
     static const sp<GraphicBuffer> kBuffer;
-    static const sp<GraphicBuffer> kOverrideBuffer;
+    std::shared_ptr<renderengine::ExternalTexture> kOverrideBuffer;
     static const sp<Fence> kFence;
     static const sp<Fence> kOverrideFence;
     static const std::string kLayerGenericMetadata1Key;
@@ -724,6 +725,11 @@
     static const std::vector<uint8_t> kLayerGenericMetadata2Value;
 
     OutputLayerWriteStateToHWCTest() {
+        kOverrideBuffer = std::make_shared<
+                renderengine::ExternalTexture>(new GraphicBuffer(), mRenderEngine,
+                                               renderengine::ExternalTexture::Usage::READABLE |
+                                                       renderengine::ExternalTexture::Usage::
+                                                               WRITEABLE);
         auto& outputLayerState = mOutputLayer.editState();
         outputLayerState.hwc = impl::OutputLayerCompositionState::Hwc(mHwcLayer);
 
@@ -839,6 +845,7 @@
 
     std::shared_ptr<HWC2::mock::Layer> mHwcLayer{std::make_shared<StrictMock<HWC2::mock::Layer>>()};
     StrictMock<mock::DisplayColorProfile> mDisplayColorProfile;
+    renderengine::mock::RenderEngine mRenderEngine;
 };
 
 const half4 OutputLayerWriteStateToHWCTest::kColor{81.f / 255.f, 82.f / 255.f, 83.f / 255.f,
@@ -858,7 +865,6 @@
 native_handle_t* OutputLayerWriteStateToHWCTest::kSidebandStreamHandle =
         reinterpret_cast<native_handle_t*>(1031);
 const sp<GraphicBuffer> OutputLayerWriteStateToHWCTest::kBuffer;
-const sp<GraphicBuffer> OutputLayerWriteStateToHWCTest::kOverrideBuffer = new GraphicBuffer();
 const sp<Fence> OutputLayerWriteStateToHWCTest::kFence;
 const sp<Fence> OutputLayerWriteStateToHWCTest::kOverrideFence = new Fence();
 const std::string OutputLayerWriteStateToHWCTest::kLayerGenericMetadata1Key =
@@ -1023,7 +1029,7 @@
                               kOverrideBufferTransform, kOverrideBlendMode, kOverrideAlpha);
     expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
                               kOverrideSurfaceDamage);
-    expectSetHdrMetadataAndBufferCalls(kOverrideBuffer, kOverrideFence);
+    expectSetHdrMetadataAndBufferCalls(kOverrideBuffer->getBuffer(), kOverrideFence);
     expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
 
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 5f0b0ee..e80100c 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#include <cmath>
-
 #include <android-base/stringprintf.h>
 #include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/impl/Output.h>
@@ -31,9 +29,12 @@
 #include <ui/Rect.h>
 #include <ui/Region.h>
 
+#include <cmath>
+
 #include "CallOrderStateMachineHelper.h"
 #include "MockHWC2.h"
 #include "RegionMatcher.h"
+#include "renderengine/ExternalTexture.h"
 
 namespace android::compositionengine {
 namespace {
@@ -2960,7 +2961,10 @@
     mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
     mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
     StrictMock<OutputPartialMock> mOutput;
-    sp<GraphicBuffer> mOutputBuffer = new GraphicBuffer();
+    std::shared_ptr<renderengine::ExternalTexture> mOutputBuffer = std::make_shared<
+            renderengine::ExternalTexture>(new GraphicBuffer(), mRenderEngine,
+                                           renderengine::ExternalTexture::Usage::READABLE |
+                                                   renderengine::ExternalTexture::Usage::WRITEABLE);
 
     std::optional<base::unique_fd> mReadyFence;
 };
@@ -3173,7 +3177,10 @@
     EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
             .WillRepeatedly(Return());
 
-    sp<GraphicBuffer> otherOutputBuffer = new GraphicBuffer();
+    const auto otherOutputBuffer = std::make_shared<
+            renderengine::ExternalTexture>(new GraphicBuffer(), mRenderEngine,
+                                           renderengine::ExternalTexture::Usage::READABLE |
+                                                   renderengine::ExternalTexture::Usage::WRITEABLE);
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_))
             .WillOnce(Return(mOutputBuffer))
             .WillOnce(Return(otherOutputBuffer));
diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
index 5ef5d7b..9aeb290 100644
--- a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
@@ -15,6 +15,8 @@
  */
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
+#include "renderengine/ExternalTexture.h"
+#include "ui/GraphicBuffer.h"
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wextra"
 
@@ -239,9 +241,9 @@
                     DoAll(SetArgPointee<0>(buffer.get()), SetArgPointee<1>(-1), Return(NO_ERROR)));
 
     base::unique_fd fence;
-    EXPECT_EQ(buffer.get(), mSurface.dequeueBuffer(&fence).get());
+    EXPECT_EQ(buffer.get(), mSurface.dequeueBuffer(&fence)->getBuffer().get());
 
-    EXPECT_EQ(buffer.get(), mSurface.mutableGraphicBufferForTest().get());
+    EXPECT_EQ(buffer.get(), mSurface.mutableTextureForTest()->getBuffer().get());
 }
 
 /*
@@ -249,8 +251,11 @@
  */
 
 TEST_F(RenderSurfaceTest, queueBufferHandlesNoClientComposition) {
-    sp<GraphicBuffer> buffer = new GraphicBuffer();
-    mSurface.mutableGraphicBufferForTest() = buffer;
+    const auto buffer = std::make_shared<
+            renderengine::ExternalTexture>(new GraphicBuffer(), mRenderEngine,
+                                           renderengine::ExternalTexture::Usage::READABLE |
+                                                   renderengine::ExternalTexture::Usage::WRITEABLE);
+    mSurface.mutableTextureForTest() = buffer;
 
     impl::OutputCompositionState state;
     state.usesClientComposition = false;
@@ -261,43 +266,45 @@
 
     mSurface.queueBuffer(base::unique_fd());
 
-    EXPECT_EQ(buffer.get(), mSurface.mutableGraphicBufferForTest().get());
+    EXPECT_EQ(buffer.get(), mSurface.mutableTextureForTest().get());
 }
 
 TEST_F(RenderSurfaceTest, queueBufferHandlesClientComposition) {
-    sp<GraphicBuffer> buffer = new GraphicBuffer();
-    mSurface.mutableGraphicBufferForTest() = buffer;
+    const auto buffer = std::make_shared<renderengine::ExternalTexture>(new GraphicBuffer(),
+                                                                        mRenderEngine, false);
+    mSurface.mutableTextureForTest() = buffer;
 
     impl::OutputCompositionState state;
     state.usesClientComposition = true;
     state.flipClientTarget = false;
 
     EXPECT_CALL(mDisplay, getState()).WillOnce(ReturnRef(state));
-    EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getNativeBuffer(), -1))
+    EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getBuffer()->getNativeBuffer(), -1))
             .WillOnce(Return(NO_ERROR));
     EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
 
     mSurface.queueBuffer(base::unique_fd());
 
-    EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
+    EXPECT_EQ(nullptr, mSurface.mutableTextureForTest().get());
 }
 
 TEST_F(RenderSurfaceTest, queueBufferHandlesFlipClientTargetRequest) {
-    sp<GraphicBuffer> buffer = new GraphicBuffer();
-    mSurface.mutableGraphicBufferForTest() = buffer;
+    const auto buffer = std::make_shared<renderengine::ExternalTexture>(new GraphicBuffer(),
+                                                                        mRenderEngine, false);
+    mSurface.mutableTextureForTest() = buffer;
 
     impl::OutputCompositionState state;
     state.usesClientComposition = false;
     state.flipClientTarget = true;
 
     EXPECT_CALL(mDisplay, getState()).WillOnce(ReturnRef(state));
-    EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getNativeBuffer(), -1))
+    EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getBuffer()->getNativeBuffer(), -1))
             .WillOnce(Return(NO_ERROR));
     EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
 
     mSurface.queueBuffer(base::unique_fd());
 
-    EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
+    EXPECT_EQ(nullptr, mSurface.mutableTextureForTest().get());
 }
 
 TEST_F(RenderSurfaceTest, queueBufferHandlesFlipClientTargetRequestWithNoBufferYetDequeued) {
@@ -317,27 +324,28 @@
 
     mSurface.queueBuffer(base::unique_fd());
 
-    EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
+    EXPECT_EQ(nullptr, mSurface.mutableTextureForTest().get());
 }
 
 TEST_F(RenderSurfaceTest, queueBufferHandlesNativeWindowQueueBufferFailureOnVirtualDisplay) {
-    sp<GraphicBuffer> buffer = new GraphicBuffer();
-    mSurface.mutableGraphicBufferForTest() = buffer;
+    const auto buffer = std::make_shared<renderengine::ExternalTexture>(new GraphicBuffer(),
+                                                                        mRenderEngine, false);
+    mSurface.mutableTextureForTest() = buffer;
 
     impl::OutputCompositionState state;
     state.usesClientComposition = true;
 
     EXPECT_CALL(mDisplay, getState()).WillOnce(ReturnRef(state));
-    EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getNativeBuffer(), -1))
+    EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getBuffer()->getNativeBuffer(), -1))
             .WillOnce(Return(INVALID_OPERATION));
     EXPECT_CALL(mDisplay, isVirtual()).WillOnce(Return(true));
-    EXPECT_CALL(*mNativeWindow, cancelBuffer(buffer->getNativeBuffer(), -1))
+    EXPECT_CALL(*mNativeWindow, cancelBuffer(buffer->getBuffer()->getNativeBuffer(), -1))
             .WillOnce(Return(NO_ERROR));
     EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
 
     mSurface.queueBuffer(base::unique_fd());
 
-    EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
+    EXPECT_EQ(nullptr, mSurface.mutableTextureForTest().get());
 }
 
 /*
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index f01fe27..283c692 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -305,8 +305,8 @@
 
     const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings,
                                 const std::vector<const renderengine::LayerSettings*>& layers,
-                                const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
-                                base::unique_fd*) -> size_t {
+                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                                base::unique_fd&&, base::unique_fd*) -> size_t {
         EXPECT_EQ(Rect(0, 0, 2, 2), displaySettings.physicalDisplay);
         EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip);
         EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation),
@@ -321,7 +321,6 @@
     EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1));
     EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
-    EXPECT_CALL(mRenderEngine, cacheExternalTextureBuffer(_));
     cachedSet.render(mRenderEngine, mOutputState);
     expectReadyBuffer(cachedSet);
 
@@ -331,7 +330,6 @@
               cachedSet.getOutputSpace().bounds);
 
     // Now check that appending a new cached set properly cleans up RenderEngine resources.
-    EXPECT_CALL(mRenderEngine, unbindExternalTextureBuffer(_));
     CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
     cachedSet.append(CachedSet(layer3));
 }
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index b7b2cc6..8692ee6 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -70,11 +70,13 @@
         mIsPrimary(args.isPrimary) {
     mCompositionDisplay->editState().isSecure = args.isSecure;
     mCompositionDisplay->createRenderSurface(
-            compositionengine::RenderSurfaceCreationArgs{ANativeWindow_getWidth(
-                                                                 args.nativeWindow.get()),
-                                                         ANativeWindow_getHeight(
-                                                                 args.nativeWindow.get()),
-                                                         args.nativeWindow, args.displaySurface});
+            compositionengine::
+                    RenderSurfaceCreationArgs{ANativeWindow_getWidth(args.nativeWindow.get()),
+                                              ANativeWindow_getHeight(args.nativeWindow.get()),
+                                              args.nativeWindow, args.displaySurface,
+                                              static_cast<size_t>(
+                                                      SurfaceFlinger::
+                                                              maxFrameBufferAcquiredBuffers)});
 
     if (!mFlinger->mDisableClientCompositionCache &&
         SurfaceFlinger::maxFrameBufferAcquiredBuffers > 0) {
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 829b916..b12e3fb 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1050,7 +1050,7 @@
     }
 
     // Allow BufferStateLayer to release any unlatched buffers in drawing state.
-    bufferMayChange(c.buffer);
+    bufferMayChange(c.buffer->getBuffer());
 
     // Commit the transaction
     commitTransaction(c);
@@ -1062,7 +1062,11 @@
 
 void Layer::commitTransaction(State& stateToCommit) {
     if (auto& bufferSurfaceFrame = mDrawingState.bufferSurfaceFrameTX;
-        mDrawingState.buffer != stateToCommit.buffer && bufferSurfaceFrame != nullptr &&
+        ((mDrawingState.buffer && stateToCommit.buffer &&
+          mDrawingState.buffer->getBuffer() != stateToCommit.buffer->getBuffer()) ||
+         (mDrawingState.buffer && !stateToCommit.buffer) ||
+         (!mDrawingState.buffer && stateToCommit.buffer)) &&
+        bufferSurfaceFrame != nullptr &&
         bufferSurfaceFrame->getPresentState() != PresentState::Presented) {
         // If the previous buffer was committed but not latched (refreshPending - happens during
         // back to back invalidates), it gets silently dropped here. Mark the corresponding
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 64986af..8e51e41 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -208,7 +208,7 @@
 
         Region transparentRegionHint;
 
-        sp<GraphicBuffer> buffer;
+        std::shared_ptr<renderengine::ExternalTexture> buffer;
         client_cache_t clientCacheId;
         sp<Fence> acquireFence;
         std::shared_ptr<FenceTime> acquireFenceTime;
@@ -412,10 +412,11 @@
     // Used only to set BufferStateLayer state
     virtual bool setTransform(uint32_t /*transform*/) { return false; };
     virtual bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/) { return false; };
-    virtual bool setBuffer(const sp<GraphicBuffer>& /*buffer*/, const sp<Fence>& /*acquireFence*/,
-                           nsecs_t /*postTime*/, nsecs_t /*desiredPresentTime*/,
-                           bool /*isAutoTimestamp*/, const client_cache_t& /*clientCacheId*/,
-                           uint64_t /* frameNumber */, std::optional<nsecs_t> /* dequeueTime */,
+    virtual bool setBuffer(const std::shared_ptr<renderengine::ExternalTexture>& /*buffer*/,
+                           const sp<Fence>& /*acquireFence*/, nsecs_t /*postTime*/,
+                           nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
+                           const client_cache_t& /*clientCacheId*/, uint64_t /* frameNumber */,
+                           std::optional<nsecs_t> /* dequeueTime */,
                            const FrameTimelineInfo& /*info*/,
                            const sp<ITransactionCompletedListener>& /* releaseBufferListener */) {
         return false;
@@ -726,7 +727,7 @@
      * Called before updating the drawing state buffer. Used by BufferStateLayer to release any
      * unlatched buffers in the drawing state.
      */
-    virtual void bufferMayChange(sp<GraphicBuffer>& /* newBuffer */){};
+    virtual void bufferMayChange(const sp<GraphicBuffer>& /* newBuffer */){};
 
     /*
      * Remove relative z for the layer if its relative parent is not part of the
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 7a3e433..a9fd16c 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -208,7 +208,8 @@
     return true;
 }
 
-const std::vector<sp<GraphicBuffer>>& RefreshRateOverlay::getOrCreateBuffers(uint32_t fps) {
+const std::vector<std::shared_ptr<renderengine::ExternalTexture>>&
+RefreshRateOverlay::getOrCreateBuffers(uint32_t fps) {
     if (mBufferCache.find(fps) == mBufferCache.end()) {
         // Ensure the range is > 0, so we don't divide by 0.
         const auto rangeLength = std::max(1u, mHighFps - mLowFps);
@@ -222,7 +223,17 @@
         color.g = HIGH_FPS_COLOR.g * fpsScale + LOW_FPS_COLOR.g * (1 - fpsScale);
         color.b = HIGH_FPS_COLOR.b * fpsScale + LOW_FPS_COLOR.b * (1 - fpsScale);
         color.a = ALPHA;
-        mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color, mShowSpinner));
+        auto buffers = SevenSegmentDrawer::drawNumber(fps, color, mShowSpinner);
+        std::vector<std::shared_ptr<renderengine::ExternalTexture>> textures;
+        std::transform(buffers.begin(), buffers.end(), std::back_inserter(textures),
+                       [&](const auto& buffer) -> std::shared_ptr<renderengine::ExternalTexture> {
+                           return std::make_shared<
+                                   renderengine::ExternalTexture>(buffer,
+                                                                  mFlinger.getRenderEngine(),
+                                                                  renderengine::ExternalTexture::
+                                                                          Usage::READABLE);
+                       });
+        mBufferCache.emplace(fps, textures);
     }
 
     return mBufferCache[fps];
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index c16cfa0..aa8329c 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -16,13 +16,14 @@
 
 #pragma once
 
-#include <unordered_map>
-
 #include <math/vec4.h>
+#include <renderengine/RenderEngine.h>
 #include <ui/Rect.h>
 #include <ui/Size.h>
 #include <utils/StrongPointer.h>
 
+#include <unordered_map>
+
 #include "Fps.h"
 
 namespace android {
@@ -70,7 +71,8 @@
     };
 
     bool createLayer();
-    const std::vector<sp<GraphicBuffer>>& getOrCreateBuffers(uint32_t fps);
+    const std::vector<std::shared_ptr<renderengine::ExternalTexture>>& getOrCreateBuffers(
+            uint32_t fps);
 
     SurfaceFlinger& mFlinger;
     const sp<Client> mClient;
@@ -78,7 +80,8 @@
     sp<IBinder> mIBinder;
     sp<IGraphicBufferProducer> mGbp;
 
-    std::unordered_map<int, std::vector<sp<GraphicBuffer>>> mBufferCache;
+    std::unordered_map<int, std::vector<std::shared_ptr<renderengine::ExternalTexture>>>
+            mBufferCache;
     std::optional<int> mCurrentFps;
     int mFrame = 0;
     static constexpr float ALPHA = 0.8f;
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index d0032ac..00090d9 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -438,18 +438,22 @@
         mFlinger.traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, filterVisitor);
     };
 
-    sp<GraphicBuffer> buffer = nullptr;
-    if (mCachedBuffer && mCachedBuffer->getWidth() == sampledBounds.getWidth() &&
-        mCachedBuffer->getHeight() == sampledBounds.getHeight()) {
+    std::shared_ptr<renderengine::ExternalTexture> buffer = nullptr;
+    if (mCachedBuffer && mCachedBuffer->getBuffer()->getWidth() == sampledBounds.getWidth() &&
+        mCachedBuffer->getBuffer()->getHeight() == sampledBounds.getHeight()) {
         buffer = mCachedBuffer;
     } else {
         const uint32_t usage =
                 GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
-        buffer = new GraphicBuffer(sampledBounds.getWidth(), sampledBounds.getHeight(),
-                                   PIXEL_FORMAT_RGBA_8888, 1, usage, "RegionSamplingThread");
-        const status_t bufferStatus = buffer->initCheck();
+        sp<GraphicBuffer> graphicBuffer =
+                new GraphicBuffer(sampledBounds.getWidth(), sampledBounds.getHeight(),
+                                  PIXEL_FORMAT_RGBA_8888, 1, usage, "RegionSamplingThread");
+        const status_t bufferStatus = graphicBuffer->initCheck();
         LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "captureSample: Buffer failed to allocate: %d",
                             bufferStatus);
+        buffer = std::make_shared<
+                renderengine::ExternalTexture>(graphicBuffer, mFlinger.getRenderEngine(),
+                                               renderengine::ExternalTexture::Usage::WRITEABLE);
     }
 
     const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
@@ -465,8 +469,8 @@
     }
 
     ALOGV("Sampling %zu descriptors", activeDescriptors.size());
-    std::vector<float> lumas =
-            sampleBuffer(buffer, sampledBounds.leftTop(), activeDescriptors, orientation);
+    std::vector<float> lumas = sampleBuffer(buffer->getBuffer(), sampledBounds.leftTop(),
+                                            activeDescriptors, orientation);
     if (lumas.size() != activeDescriptors.size()) {
         ALOGW("collected %zu median luma values for %zu descriptors", lumas.size(),
               activeDescriptors.size());
@@ -477,16 +481,6 @@
         activeDescriptors[d].listener->onSampleCollected(lumas[d]);
     }
 
-    // Extend the lifetime of mCachedBuffer from the previous frame to here to ensure that:
-    // 1) The region sampling thread is the last owner of the buffer, and the freeing of the buffer
-    // happens in this thread, as opposed to the main thread.
-    // 2) The listener(s) receive their notifications prior to freeing the buffer.
-    if (mCachedBuffer != nullptr && mCachedBuffer != buffer) {
-        if (mFlinger.getRenderEngine().getRenderEngineType() ==
-            renderengine::RenderEngine::RenderEngineType::SKIA_GL_THREADED) {
-            mFlinger.getRenderEngine().unbindExternalTextureBuffer(mCachedBuffer->getId());
-        }
-    }
     mCachedBuffer = buffer;
     ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::noWorkNeeded));
 }
diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h
index 0defdb3..86632db 100644
--- a/services/surfaceflinger/RegionSamplingThread.h
+++ b/services/surfaceflinger/RegionSamplingThread.h
@@ -16,17 +16,19 @@
 
 #pragma once
 
+#include <android-base/thread_annotations.h>
+#include <binder/IBinder.h>
+#include <renderengine/ExternalTexture.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/Rect.h>
+#include <utils/StrongPointer.h>
+
 #include <chrono>
 #include <condition_variable>
 #include <mutex>
 #include <thread>
 #include <unordered_map>
 
-#include <android-base/thread_annotations.h>
-#include <binder/IBinder.h>
-#include <ui/GraphicBuffer.h>
-#include <ui/Rect.h>
-#include <utils/StrongPointer.h>
 #include "Scheduler/OneShotTimer.h"
 
 namespace android {
@@ -122,7 +124,8 @@
 
     std::mutex mSamplingMutex;
     std::unordered_map<wp<IBinder>, Descriptor, WpHash> mDescriptors GUARDED_BY(mSamplingMutex);
-    sp<GraphicBuffer> mCachedBuffer GUARDED_BY(mSamplingMutex) = nullptr;
+    std::shared_ptr<renderengine::ExternalTexture> mCachedBuffer GUARDED_BY(mSamplingMutex) =
+            nullptr;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 02579c6..e66f65b 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -735,6 +735,7 @@
     mCompositionEngine->setTimeStats(mTimeStats);
     mCompositionEngine->setHwComposer(getFactory().createHWComposer(getBE().mHwcServiceName));
     mCompositionEngine->getHwComposer().setConfiguration(this, getBE().mComposerSequenceId);
+    ClientCache::getInstance().setRenderEngine(&getRenderEngine());
     // Process any initial hotplug and resulting display changes.
     processDisplayHotplugEventsLocked();
     const auto display = getDefaultDisplayDeviceLocked();
@@ -3707,7 +3708,6 @@
 
     if (uncacheBuffer.isValid()) {
         ClientCache::getInstance().erase(uncacheBuffer);
-        getRenderEngine().unbindExternalTextureBuffer(uncacheBuffer.id);
     }
 
     // If a synchronous transaction is explicitly requested without any changes, force a transaction
@@ -4083,23 +4083,16 @@
     }
     bool bufferChanged = what & layer_state_t::eBufferChanged;
     bool cacheIdChanged = what & layer_state_t::eCachedBufferChanged;
-    sp<GraphicBuffer> buffer;
+    std::shared_ptr<renderengine::ExternalTexture> buffer;
     if (bufferChanged && cacheIdChanged && s.buffer != nullptr) {
-        buffer = s.buffer;
-        bool success = ClientCache::getInstance().add(s.cachedBuffer, s.buffer);
-        if (success) {
-            getRenderEngine().cacheExternalTextureBuffer(s.buffer);
-            success = ClientCache::getInstance()
-                              .registerErasedRecipient(s.cachedBuffer,
-                                                       wp<ClientCache::ErasedRecipient>(this));
-            if (!success) {
-                getRenderEngine().unbindExternalTextureBuffer(s.buffer->getId());
-            }
-        }
+        ClientCache::getInstance().add(s.cachedBuffer, s.buffer);
+        buffer = ClientCache::getInstance().get(s.cachedBuffer);
     } else if (cacheIdChanged) {
         buffer = ClientCache::getInstance().get(s.cachedBuffer);
     } else if (bufferChanged) {
-        buffer = s.buffer;
+        buffer = std::make_shared<
+                renderengine::ExternalTexture>(s.buffer, getRenderEngine(),
+                                               renderengine::ExternalTexture::Usage::READABLE);
     }
     if (buffer) {
         const bool frameNumberChanged = what & layer_state_t::eFrameNumberChanged;
@@ -6003,15 +5996,17 @@
     const status_t bufferStatus = buffer->initCheck();
     LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "captureScreenCommon: Buffer failed to allocate: %d",
                         bufferStatus);
-    return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer,
+    const auto texture = std::make_shared<
+            renderengine::ExternalTexture>(buffer, getRenderEngine(),
+                                           renderengine::ExternalTexture::Usage::WRITEABLE);
+    return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, texture,
                                false /* regionSampling */, grayscale, captureListener);
 }
 
-status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture,
-                                             TraverseLayersFunction traverseLayers,
-                                             sp<GraphicBuffer>& buffer, bool regionSampling,
-                                             bool grayscale,
-                                             const sp<IScreenCaptureListener>& captureListener) {
+status_t SurfaceFlinger::captureScreenCommon(
+        RenderAreaFuture renderAreaFuture, TraverseLayersFunction traverseLayers,
+        const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
+        bool grayscale, const sp<IScreenCaptureListener>& captureListener) {
     ATRACE_CALL();
 
     if (captureListener == nullptr) {
@@ -6044,15 +6039,6 @@
                                             regionSampling, grayscale, captureResults);
         });
 
-        // TODO(b/180767535): Remove this once we optimize buffer lifecycle for RenderEngine
-        // Only do this when we're not doing region sampling, to allow the region sampling thread to
-        // manage buffer lifecycle itself.
-        if (!regionSampling &&
-            getRenderEngine().getRenderEngineType() ==
-                    renderengine::RenderEngine::RenderEngineType::SKIA_GL_THREADED) {
-            getRenderEngine().unbindExternalTextureBuffer(buffer->getId());
-        }
-
         captureResults.result = result;
         captureListener->onScreenCaptureCompleted(captureResults);
     }));
@@ -6060,11 +6046,10 @@
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea,
-                                                TraverseLayersFunction traverseLayers,
-                                                const sp<GraphicBuffer>& buffer, bool forSystem,
-                                                bool regionSampling, bool grayscale,
-                                                ScreenCaptureResults& captureResults) {
+status_t SurfaceFlinger::renderScreenImplLocked(
+        const RenderArea& renderArea, TraverseLayersFunction traverseLayers,
+        const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool forSystem,
+        bool regionSampling, bool grayscale, ScreenCaptureResults& captureResults) {
     ATRACE_CALL();
 
     traverseLayers([&](Layer* layer) {
@@ -6072,7 +6057,7 @@
                 captureResults.capturedSecureLayers || (layer->isVisible() && layer->isSecure());
     });
 
-    const bool useProtected = buffer->getUsage() & GRALLOC_USAGE_PROTECTED;
+    const bool useProtected = buffer->getBuffer()->getUsage() & GRALLOC_USAGE_PROTECTED;
 
     // We allow the system server to take screenshots of secure layers for
     // use in situations like the Screen-rotation animation and place
@@ -6082,7 +6067,7 @@
         return PERMISSION_DENIED;
     }
 
-    captureResults.buffer = buffer;
+    captureResults.buffer = buffer->getBuffer();
     captureResults.capturedDataspace = renderArea.getReqDataSpace();
 
     const auto reqWidth = renderArea.getReqWidth();
@@ -6173,11 +6158,9 @@
     base::unique_fd drawFence;
     getRenderEngine().useProtectedContext(useProtected);
 
-    // TODO(b/180767535): Remove this once we optimize buffer lifecycle for RenderEngine
-    const bool useFramebufferCache = getRenderEngine().getRenderEngineType() ==
-            renderengine::RenderEngine::RenderEngineType::SKIA_GL_THREADED;
+    const constexpr bool kUseFramebufferCache = false;
     getRenderEngine().drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, buffer,
-                                 useFramebufferCache, std::move(bufferFence), &drawFence);
+                                 kUseFramebufferCache, std::move(bufferFence), &drawFence);
 
     if (drawFence >= 0) {
         sp<Fence> releaseFence = new Fence(dup(drawFence));
@@ -6442,10 +6425,6 @@
     mOffscreenLayers.erase(layer);
 }
 
-void SurfaceFlinger::bufferErased(const client_cache_t& clientCacheId) {
-    getRenderEngine().unbindExternalTextureBuffer(clientCacheId.id);
-}
-
 status_t SurfaceFlinger::setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
                                                  float lightPosY, float lightPosZ,
                                                  float lightRadius) {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index b3da61e..a242d8a 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -184,7 +184,6 @@
 
 class SurfaceFlinger : public BnSurfaceComposer,
                        public PriorityDumper,
-                       public ClientCache::ErasedRecipient,
                        private IBinder::DeathRecipient,
                        private HWC2::ComposerCallback,
                        private ISchedulerCallback {
@@ -332,9 +331,6 @@
     wp<Layer> fromHandle(const sp<IBinder>& handle);
     wp<Layer> fromHandleLocked(const sp<IBinder>& handle) const REQUIRES(mStateLock);
 
-    // Inherit from ClientCache::ErasedRecipient
-    void bufferErased(const client_cache_t& clientCacheId) override;
-
     // If set, disables reusing client composition buffers. This can be set by
     // debug.sf.disable_client_composition_cache
     bool mDisableClientCompositionCache = false;
@@ -907,12 +903,14 @@
     status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction, ui::Size bufferSize,
                                  ui::PixelFormat, bool allowProtected, bool grayscale,
                                  const sp<IScreenCaptureListener>&);
-    status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction, sp<GraphicBuffer>&,
+    status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction,
+                                 const std::shared_ptr<renderengine::ExternalTexture>&,
                                  bool regionSampling, bool grayscale,
                                  const sp<IScreenCaptureListener>&);
     status_t renderScreenImplLocked(const RenderArea&, TraverseLayersFunction,
-                                    const sp<GraphicBuffer>&, bool forSystem, bool regionSampling,
-                                    bool grayscale, ScreenCaptureResults&);
+                                    const std::shared_ptr<renderengine::ExternalTexture>&,
+                                    bool forSystem, bool regionSampling, bool grayscale,
+                                    ScreenCaptureResults&);
 
     sp<DisplayDevice> getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) REQUIRES(mStateLock);
     sp<DisplayDevice> getDisplayById(DisplayId displayId) const REQUIRES(mStateLock);
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 4e1c0c7..3042450 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -15,6 +15,7 @@
  */
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
+#include "renderengine/ExternalTexture.h"
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
 #pragma clang diagnostic ignored "-Wextra"
@@ -194,7 +195,7 @@
 
     sp<Fence> mClientTargetAcquireFence = Fence::NO_FENCE;
 
-    sp<GraphicBuffer> mCaptureScreenBuffer;
+    std::shared_ptr<renderengine::ExternalTexture> mCaptureScreenBuffer;
 };
 
 template <typename LayerCase>
@@ -243,11 +244,15 @@
     // TODO: Eliminate expensive/real allocation if possible.
     const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
             GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
-    mCaptureScreenBuffer = new GraphicBuffer(renderArea->getReqWidth(), renderArea->getReqHeight(),
-                                             HAL_PIXEL_FORMAT_RGBA_8888, 1, usage, "screenshot");
+    mCaptureScreenBuffer = std::make_shared<
+            renderengine::ExternalTexture>(new GraphicBuffer(renderArea->getReqWidth(),
+                                                             renderArea->getReqHeight(),
+                                                             HAL_PIXEL_FORMAT_RGBA_8888, 1, usage,
+                                                             "screenshot"),
+                                           *mRenderEngine, true);
 
     status_t result =
-            mFlinger.renderScreenImplLocked(*renderArea, traverseLayers, mCaptureScreenBuffer.get(),
+            mFlinger.renderScreenImplLocked(*renderArea, traverseLayers, mCaptureScreenBuffer,
                                             forSystem, regionSampling);
     EXPECT_EQ(NO_ERROR, result);
 
@@ -340,8 +345,8 @@
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
                 .WillRepeatedly([](const renderengine::DisplaySettings& displaySettings,
                                    const std::vector<const renderengine::LayerSettings*>&,
-                                   const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
-                                   base::unique_fd*) -> status_t {
+                                   const std::shared_ptr<renderengine::ExternalTexture>&,
+                                   const bool, base::unique_fd&&, base::unique_fd*) -> status_t {
                     EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
                     EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
                               displaySettings.physicalDisplay);
@@ -389,8 +394,8 @@
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
                 .WillRepeatedly([](const renderengine::DisplaySettings& displaySettings,
                                    const std::vector<const renderengine::LayerSettings*>&,
-                                   const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
-                                   base::unique_fd*) -> status_t {
+                                   const std::shared_ptr<renderengine::ExternalTexture>&,
+                                   const bool, base::unique_fd&&, base::unique_fd*) -> status_t {
                     EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
                     EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
                               displaySettings.physicalDisplay);
@@ -625,8 +630,8 @@
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
                 .WillOnce([](const renderengine::DisplaySettings& displaySettings,
                              const std::vector<const renderengine::LayerSettings*>& layerSettings,
-                             const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
-                             base::unique_fd*) -> status_t {
+                             const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                             base::unique_fd&&, base::unique_fd*) -> status_t {
                     EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
                     EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
                               displaySettings.physicalDisplay);
@@ -674,8 +679,8 @@
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
                 .WillOnce([](const renderengine::DisplaySettings& displaySettings,
                              const std::vector<const renderengine::LayerSettings*>& layerSettings,
-                             const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
-                             base::unique_fd*) -> status_t {
+                             const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                             base::unique_fd&&, base::unique_fd*) -> status_t {
                     EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
                     EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
                               displaySettings.physicalDisplay);
@@ -751,8 +756,8 @@
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
                 .WillOnce([](const renderengine::DisplaySettings& displaySettings,
                              const std::vector<const renderengine::LayerSettings*>& layerSettings,
-                             const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
-                             base::unique_fd*) -> status_t {
+                             const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                             base::unique_fd&&, base::unique_fd*) -> status_t {
                     EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
                     EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
                               displaySettings.physicalDisplay);
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 63baf7d..d004b9d 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -352,8 +352,8 @@
 
     auto renderScreenImplLocked(const RenderArea& renderArea,
                                 SurfaceFlinger::TraverseLayersFunction traverseLayers,
-                                const sp<GraphicBuffer>& buffer, bool forSystem,
-                                bool regionSampling) {
+                                const std::shared_ptr<renderengine::ExternalTexture>& buffer,
+                                bool forSystem, bool regionSampling) {
         ScreenCaptureResults captureResults;
         return mFlinger->renderScreenImplLocked(renderArea, traverseLayers, buffer, forSystem,
                                                 regionSampling, false /* grayscale */,
diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
index b5ef0a1..690a7fd 100644
--- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
@@ -21,6 +21,8 @@
 #include <gtest/gtest.h>
 #include <gui/SurfaceComposerClient.h>
 #include <log/log.h>
+#include <renderengine/ExternalTexture.h>
+#include <renderengine/mock/RenderEngine.h>
 #include <utils/String8.h>
 
 #include "TestableSurfaceFlinger.h"
@@ -102,6 +104,7 @@
 
     TestableSurfaceFlinger mFlinger;
     Hwc2::mock::Composer* mComposer = nullptr;
+    renderengine::mock::RenderEngine mRenderEngine;
     FenceToFenceTimeMap fenceFactory;
     client_cache_t mClientCache;
 
@@ -109,9 +112,12 @@
         sp<BufferStateLayer> layer = createBufferStateLayer();
 
         sp<Fence> fence(new Fence());
-        sp<GraphicBuffer> buffer{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+        const auto buffer = std::make_shared<
+                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+                                                                 1, 0),
+                                               mRenderEngine, false);
         int32_t layerId = layer->getSequence();
-        uint64_t bufferId = buffer->getId();
+        uint64_t bufferId = buffer->getBuffer()->getId();
         uint64_t frameNumber = 5;
         nsecs_t dequeueTime = 10;
         nsecs_t postTime = 20;
diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
index c75538f..6ff6e90 100644
--- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
@@ -21,6 +21,8 @@
 #include <gtest/gtest.h>
 #include <gui/SurfaceComposerClient.h>
 #include <log/log.h>
+#include <renderengine/ExternalTexture.h>
+#include <renderengine/mock/RenderEngine.h>
 #include <utils/String8.h>
 
 #include "TestableSurfaceFlinger.h"
@@ -104,6 +106,7 @@
 
     TestableSurfaceFlinger mFlinger;
     Hwc2::mock::Composer* mComposer = nullptr;
+    renderengine::mock::RenderEngine mRenderEngine;
     FenceToFenceTimeMap fenceFactory;
     client_cache_t mClientCache;
 
@@ -124,7 +127,10 @@
         sp<BufferStateLayer> layer = createBufferStateLayer();
         sp<Fence> fence(new Fence());
         auto acquireFence = fenceFactory.createFenceTimeForTest(fence);
-        sp<GraphicBuffer> buffer{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+        const auto buffer = std::make_shared<
+                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+                                                                 1, 0),
+                                               mRenderEngine, false);
         layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
                          {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         acquireFence->signalForTest(12);
@@ -149,7 +155,10 @@
 
         sp<Fence> fence1(new Fence());
         auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
-        sp<GraphicBuffer> buffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+        const auto buffer1 = std::make_shared<
+                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+                                                                 1, 0),
+                                               mRenderEngine, false);
         layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
                          {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         EXPECT_EQ(0u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
@@ -158,7 +167,10 @@
 
         sp<Fence> fence2(new Fence());
         auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2);
-        sp<GraphicBuffer> buffer2{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+        const auto buffer2 = std::make_shared<
+                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+                                                                 1, 0),
+                                               mRenderEngine, false);
         nsecs_t start = systemTime();
         layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt,
                          {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
@@ -196,8 +208,10 @@
 
         sp<Fence> fence(new Fence());
         auto acquireFence = fenceFactory.createFenceTimeForTest(fence);
-        sp<GraphicBuffer> buffer{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
-
+        const auto buffer = std::make_shared<
+                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+                                                                 1, 0),
+                                               mRenderEngine, false);
         layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
                          {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         acquireFence->signalForTest(12);
@@ -222,8 +236,10 @@
         sp<BufferStateLayer> layer = createBufferStateLayer();
         sp<Fence> fence(new Fence());
         auto acquireFence = fenceFactory.createFenceTimeForTest(fence);
-        sp<GraphicBuffer> buffer{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
-
+        const auto buffer = std::make_shared<
+                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+                                                                 1, 0),
+                                               mRenderEngine, false);
         layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
                          {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         EXPECT_EQ(0u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
@@ -252,8 +268,10 @@
 
         sp<Fence> fence(new Fence());
         auto acquireFence = fenceFactory.createFenceTimeForTest(fence);
-        sp<GraphicBuffer> buffer{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
-
+        const auto buffer = std::make_shared<
+                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+                                                                 1, 0),
+                                               mRenderEngine, false);
         layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
                          {/*vsyncId*/ 3, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         EXPECT_EQ(2u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
@@ -351,7 +369,10 @@
 
         sp<Fence> fence1(new Fence());
         auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
-        sp<GraphicBuffer> buffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+        const auto buffer1 = std::make_shared<
+                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+                                                                 1, 0),
+                                               mRenderEngine, false);
         layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
                          {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
@@ -359,7 +380,10 @@
 
         sp<Fence> fence2(new Fence());
         auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2);
-        sp<GraphicBuffer> buffer2{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+        const auto buffer2 = std::make_shared<
+                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+                                                                 1, 0),
+                                               mRenderEngine, false);
         layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt,
                          {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         acquireFence2->signalForTest(12);
@@ -386,7 +410,10 @@
 
         sp<Fence> fence1(new Fence());
         auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
-        sp<GraphicBuffer> buffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+        const auto buffer1 = std::make_shared<
+                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+                                                                 1, 0),
+                                               mRenderEngine, false);
         layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
                          {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         EXPECT_EQ(0u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
@@ -395,7 +422,10 @@
 
         sp<Fence> fence2(new Fence());
         auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2);
-        sp<GraphicBuffer> buffer2{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+        const auto buffer2 = std::make_shared<
+                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+                                                                 1, 0),
+                                               mRenderEngine, false);
         auto dropStartTime1 = systemTime();
         layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt,
                          {/*vsyncId*/ FrameTimelineInfo::INVALID_VSYNC_ID, /*inputEventId*/ 0},
@@ -407,7 +437,10 @@
 
         sp<Fence> fence3(new Fence());
         auto acquireFence3 = fenceFactory.createFenceTimeForTest(fence3);
-        sp<GraphicBuffer> buffer3{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+        const auto buffer3 = std::make_shared<
+                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+                                                                 1, 0),
+                                               mRenderEngine, false);
         auto dropStartTime2 = systemTime();
         layer->setBuffer(buffer3, fence3, 10, 20, false, mClientCache, 1, std::nullopt,
                          {/*vsyncId*/ 2, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
@@ -447,7 +480,11 @@
         std::vector<std::shared_ptr<frametimeline::SurfaceFrame>> bufferlessSurfaceFrames;
         for (int i = 0; i < 10; i += 2) {
             sp<Fence> fence1(new Fence());
-            sp<GraphicBuffer> buffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+            const auto buffer1 = std::make_shared<
+                    renderengine::ExternalTexture>(new GraphicBuffer(1, 1,
+                                                                     HAL_PIXEL_FORMAT_RGBA_8888, 1,
+                                                                     0),
+                                                   mRenderEngine, false);
             layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
                              {/*vsyncId*/ 1, /*inputEventId*/ 0},
                              nullptr /* releaseBufferCallback */);