Revert "(Reland) Rename ColorBuffer to ColorBufferGl and add Col..."

Revert submission 2442579-revert-2383058-revert-2318952-JRCOFEFCOJ-MCGBSQEWLC

Reason for revert: Media failures b/270230549

Reverted changes: /q/submissionid:2442579-revert-2383058-revert-2318952-JRCOFEFCOJ-MCGBSQEWLC

Change-Id: I29c91ce6cb7415b73f3ea6d0a780ccc1d01894d4
diff --git a/stream-servers/Android.bp b/stream-servers/Android.bp
index 00946af..2079cd4 100644
--- a/stream-servers/Android.bp
+++ b/stream-servers/Android.bp
@@ -57,7 +57,6 @@
     ],
     srcs: [
         "ChannelStream.cpp",
-        "ColorBuffer.cpp",
         "DisplaySurface.cpp",
         "DisplaySurfaceUser.cpp",
         "Hwc2.cpp",
diff --git a/stream-servers/CMakeLists.txt b/stream-servers/CMakeLists.txt
index 864e6ba..ead0364 100644
--- a/stream-servers/CMakeLists.txt
+++ b/stream-servers/CMakeLists.txt
@@ -13,7 +13,6 @@
 
 # Stream server core
 set(stream-server-core-sources
-    ColorBuffer.cpp
     GfxStreamAgents.cpp
     VirtioGpuTimelines.cpp
     VsyncThread.cpp
diff --git a/stream-servers/ColorBuffer.cpp b/stream-servers/ColorBuffer.cpp
deleted file mode 100644
index 7d8cc68..0000000
--- a/stream-servers/ColorBuffer.cpp
+++ /dev/null
@@ -1,451 +0,0 @@
-// Copyright 2022 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either expresso or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "ColorBuffer.h"
-
-#include "gl/EmulationGl.h"
-#include "vulkan/VkCommonOperations.h"
-
-using android::base::ManagedDescriptor;
-using emugl::ABORT_REASON_OTHER;
-using emugl::FatalError;
-
-namespace {
-
-// ColorBufferVk natively supports YUV images. However, ColorBufferGl
-// needs to emulate YUV support by having an underlying RGBA texture
-// and adding in additional YUV<->RGBA conversions when needed. The
-// memory should not be shared between the VK YUV image and the GL RGBA
-// texture.
-bool shouldAttemptExternalMemorySharing(FrameworkFormat format) {
-    return format == FrameworkFormat::FRAMEWORK_FORMAT_GL_COMPATIBLE;
-}
-
-}  // namespace
-
-ColorBuffer::ColorBuffer(HandleType handle, uint32_t width, uint32_t height, GLenum format,
-                         FrameworkFormat frameworkFormat)
-    : mHandle(handle),
-      mWidth(width),
-      mHeight(height),
-      mFormat(format),
-      mFrameworkFormat(frameworkFormat) {}
-
-/*static*/
-std::shared_ptr<ColorBuffer> ColorBuffer::create(gfxstream::EmulationGl* emulationGl,
-                                                 goldfish_vk::VkEmulation* emulationVk,
-                                                 uint32_t width, uint32_t height, GLenum format,
-                                                 FrameworkFormat frameworkFormat,
-                                                 HandleType handle) {
-    std::shared_ptr<ColorBuffer> colorBuffer(
-        new ColorBuffer(handle, width, height, format, frameworkFormat));
-
-    if (emulationGl) {
-        colorBuffer->mColorBufferGl =
-            emulationGl->createColorBuffer(width, height, format, frameworkFormat, handle);
-        if (!colorBuffer->mColorBufferGl) {
-            ERR("Failed to initialize ColorBufferGl.");
-            return nullptr;
-        }
-    }
-
-    if (emulationVk && emulationVk->live) {
-        const bool vulkanOnly = colorBuffer->mColorBufferGl == nullptr;
-
-        colorBuffer->mColorBufferVk = std::make_unique<ColorBufferVk>();
-        if (!goldfish_vk::setupVkColorBuffer(width, height, format, frameworkFormat, handle,
-                                             vulkanOnly, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) {
-            ERR("Failed to initialize ColorBufferVk.");
-            return nullptr;
-        }
-    }
-
-    if (colorBuffer->mColorBufferGl && colorBuffer->mColorBufferVk &&
-        shouldAttemptExternalMemorySharing(frameworkFormat)) {
-        auto memoryExport = goldfish_vk::exportColorBufferMemory(handle);
-        if (memoryExport) {
-            if (colorBuffer->mColorBufferGl->importMemory(
-                    std::move(memoryExport->descriptor), memoryExport->size,
-                    /*dedicated=*/false, memoryExport->linearTiling)) {
-                colorBuffer->mGlAndVkAreSharingExternalMemory = true;
-            } else {
-                ERR("Failed to import memory to ColorBufferGl:%d", handle);
-                return nullptr;
-            }
-        }
-    }
-
-    return colorBuffer;
-}
-
-/*static*/
-std::shared_ptr<ColorBuffer> ColorBuffer::onLoad(gfxstream::EmulationGl* emulationGl,
-                                                 goldfish_vk::VkEmulation*,
-                                                 android::base::Stream* stream) {
-    const auto handle = static_cast<HandleType>(stream->getBe32());
-    const auto width = static_cast<uint32_t>(stream->getBe32());
-    const auto height = static_cast<uint32_t>(stream->getBe32());
-    const auto format = static_cast<GLenum>(stream->getBe32());
-    const auto frameworkFormat = static_cast<FrameworkFormat>(stream->getBe32());
-
-    std::shared_ptr<ColorBuffer> colorBuffer(
-        new ColorBuffer(handle, width, height, format, frameworkFormat));
-
-    if (emulationGl) {
-        colorBuffer->mColorBufferGl = emulationGl->loadColorBuffer(stream);
-        if (!colorBuffer->mColorBufferGl) {
-            ERR("Failed to load ColorBufferGl.");
-            return nullptr;
-        }
-    }
-
-    colorBuffer->mNeedRestore = true;
-
-    return colorBuffer;
-}
-
-void ColorBuffer::onSave(android::base::Stream* stream) {
-    stream->putBe32(getHndl());
-    stream->putBe32(mWidth);
-    stream->putBe32(mHeight);
-    stream->putBe32(static_cast<uint32_t>(mFormat));
-    stream->putBe32(static_cast<uint32_t>(mFrameworkFormat));
-
-    if (mColorBufferGl) {
-        mColorBufferGl->onSave(stream);
-    }
-}
-
-void ColorBuffer::restore() {
-    if (mColorBufferGl) {
-        mColorBufferGl->restore();
-    }
-}
-
-void ColorBuffer::readToBytes(int x, int y, int width, int height, GLenum pixelsFormat,
-                              GLenum pixelsType, void* outPixels) {
-    touch();
-
-    if (mColorBufferVk) {
-        goldfish_vk::readColorBufferToBytes(mHandle, x, y, width, height, outPixels);
-        return;
-    }
-    if (mColorBufferGl) {
-        mColorBufferGl->readPixels(x, y, width, height, pixelsFormat, pixelsType, outPixels);
-        return;
-    }
-
-    GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "No ColorBuffer impl?";
-}
-
-void ColorBuffer::readToBytesScaled(int pixelsWidth, int pixelsHeight, GLenum pixelsFormat,
-                                    GLenum pixelsType, int pixelsRotation, emugl::Rect rect,
-                                    void* outPixels) {
-    touch();
-
-    if (mColorBufferGl) {
-        mColorBufferGl->readPixelsScaled(pixelsWidth, pixelsHeight, pixelsFormat, pixelsType,
-                                         pixelsRotation, rect, outPixels);
-        return;
-    }
-
-    GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "Unimplemented.";
-}
-
-void ColorBuffer::readYuvToBytes(int x, int y, int width, int height, void* outPixels,
-                                 uint32_t pixelsSize) {
-    touch();
-
-    if (mColorBufferVk) {
-        goldfish_vk::readColorBufferToBytes(mHandle, x, y, width, height, outPixels);
-        return;
-    }
-    if (mColorBufferGl) {
-        mColorBufferGl->readPixelsYUVCached(x, y, width, height, outPixels, pixelsSize);
-        return;
-    }
-
-    GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "No ColorBuffer impl?";
-}
-
-bool ColorBuffer::updateFromBytes(int x, int y, int width, int height,
-                                  FrameworkFormat frameworkFormat, GLenum pixelsFormat,
-                                  GLenum pixelsType, const void* pixels) {
-    touch();
-
-    if (mColorBufferVk) {
-        return goldfish_vk::updateColorBufferFromBytes(mHandle, x, y, width, height, pixels);
-    }
-    if (mColorBufferGl) {
-        mColorBufferGl->subUpdateFromFrameworkFormat(x, y, width, height, frameworkFormat,
-                                                     pixelsFormat, pixelsType, pixels);
-        return true;
-    }
-
-    GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "No ColorBuffer impl?";
-    return false;
-}
-
-bool ColorBuffer::updateFromBytes(int x, int y, int width, int height, GLenum pixelsFormat,
-                                  GLenum pixelsType, const void* pixels) {
-    touch();
-
-    if (mColorBufferVk) {
-        return goldfish_vk::updateColorBufferFromBytes(mHandle, x, y, width, height, pixels);
-    }
-    if (mColorBufferGl) {
-        mColorBufferGl->subUpdate(x, y, width, height, pixelsFormat, pixelsType, pixels);
-        return true;
-    }
-
-    GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "No ColorBuffer impl?";
-    return false;
-}
-
-bool ColorBuffer::updateGlFromBytes(const void* bytes, std::size_t bytesSize) {
-    if (mColorBufferGl) {
-        touch();
-
-        return mColorBufferGl->replaceContents(bytes, bytesSize);
-    }
-
-    return true;
-}
-
-std::unique_ptr<BorrowedImageInfo> ColorBuffer::borrowForComposition(UsedApi api, bool isTarget) {
-    switch (api) {
-        case UsedApi::kGl: {
-            if (!mColorBufferGl) {
-                GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
-            }
-            return mColorBufferGl->getBorrowedImageInfo();
-        }
-        case UsedApi::kVk: {
-            if (!mColorBufferVk) {
-                GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
-            }
-            return goldfish_vk::borrowColorBufferForComposition(getHndl(), isTarget);
-        }
-    }
-    GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "Unimplemented";
-    return nullptr;
-}
-
-std::unique_ptr<BorrowedImageInfo> ColorBuffer::borrowForDisplay(UsedApi api) {
-    switch (api) {
-        case UsedApi::kGl: {
-            if (!mColorBufferGl) {
-                GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
-            }
-            return mColorBufferGl->getBorrowedImageInfo();
-        }
-        case UsedApi::kVk: {
-            if (!mColorBufferVk) {
-                GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
-            }
-            return goldfish_vk::borrowColorBufferForDisplay(getHndl());
-        }
-    }
-    GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "Unimplemented";
-    return nullptr;
-}
-
-void ColorBuffer::updateFromGl() {
-    if (!(mColorBufferGl && mColorBufferVk)) {
-        return;
-    }
-
-    if (mGlAndVkAreSharingExternalMemory) {
-        return;
-    }
-
-    std::size_t contentsSize = 0;
-    if (!mColorBufferGl->readContents(&contentsSize, nullptr)) {
-        ERR("Failed to get GL contents size for ColorBuffer:%d", mHandle);
-        return;
-    }
-
-    std::vector<uint8_t> contents(contentsSize, 0);
-
-    if (!mColorBufferGl->readContents(&contentsSize, contents.data())) {
-        ERR("Failed to get GL contents for ColorBuffer:%d", mHandle);
-        return;
-    }
-
-    if (!goldfish_vk::updateColorBufferFromBytes(mHandle, contents)) {
-        ERR("Failed to set VK contents for ColorBuffer:%d", mHandle);
-        return;
-    }
-}
-
-void ColorBuffer::updateFromVk() {
-    if (!(mColorBufferGl && mColorBufferVk)) {
-        return;
-    }
-
-    if (mGlAndVkAreSharingExternalMemory) {
-        return;
-    }
-
-    std::vector<uint8_t> contents;
-    if (!goldfish_vk::readColorBufferToBytes(mHandle, &contents)) {
-        ERR("Failed to get VK contents for ColorBuffer:%d", mHandle);
-        return;
-    }
-
-    if (contents.empty()) return;
-
-    if (!mColorBufferGl->replaceContents(contents.data(), contents.size())) {
-        ERR("Failed to set GL contents for ColorBuffer:%d", mHandle);
-        return;
-    }
-}
-
-bool ColorBuffer::glOpBlitFromCurrentReadBuffer() {
-    if (!mColorBufferGl) {
-        GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
-    }
-
-    touch();
-
-    return mColorBufferGl->blitFromCurrentReadBuffer();
-}
-
-bool ColorBuffer::glOpBindToTexture() {
-    if (!mColorBufferGl) {
-        GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
-    }
-
-    touch();
-
-    return mColorBufferGl->bindToTexture();
-}
-
-bool ColorBuffer::glOpBindToTexture2() {
-    if (!mColorBufferGl) {
-        GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
-    }
-
-    return mColorBufferGl->bindToTexture2();
-}
-
-bool ColorBuffer::glOpBindToRenderbuffer() {
-    if (!mColorBufferGl) {
-        GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
-    }
-
-    touch();
-
-    return mColorBufferGl->bindToRenderbuffer();
-}
-
-GLuint ColorBuffer::glOpGetTexture() {
-    if (!mColorBufferGl) {
-        GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
-    }
-
-    touch();
-
-    return mColorBufferGl->getTexture();
-}
-
-void ColorBuffer::glOpReadback(unsigned char* img, bool readbackBgra) {
-    if (!mColorBufferGl) {
-        GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
-    }
-
-    touch();
-
-    return mColorBufferGl->readback(img, readbackBgra);
-}
-
-void ColorBuffer::glOpReadbackAsync(GLuint buffer, bool readbackBgra) {
-    if (!mColorBufferGl) {
-        GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
-    }
-
-    touch();
-
-    mColorBufferGl->readbackAsync(buffer, readbackBgra);
-}
-
-bool ColorBuffer::glOpImportEglImage(void* image, bool preserveContent) {
-    if (!mColorBufferGl) {
-        GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
-    }
-
-    return mColorBufferGl->importEglImage(image, preserveContent);
-}
-
-bool ColorBuffer::glOpImportEglNativePixmap(void* pixmap, bool preserveContent) {
-    if (!mColorBufferGl) {
-        GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
-    }
-
-    return mColorBufferGl->importEglNativePixmap(pixmap, preserveContent);
-}
-
-void ColorBuffer::glOpSwapYuvTexturesAndUpdate(GLenum format, GLenum type,
-                                               FrameworkFormat frameworkFormat, GLuint* textures) {
-    if (!mColorBufferGl) {
-        GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
-    }
-
-    mColorBufferGl->swapYUVTextures(frameworkFormat, textures);
-
-    // This makes ColorBufferGl regenerate the RGBA texture using
-    // YUVConverter::drawConvert() with the updated YUV textures.
-    mColorBufferGl->subUpdate(0, 0, mWidth, mHeight, format, type, nullptr);
-
-    updateFromGl();
-}
-
-bool ColorBuffer::glOpReadContents(size_t* outNumBytes, void* outContents) {
-    if (!mColorBufferGl) {
-        GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
-    }
-
-    return mColorBufferGl->readContents(outNumBytes, outContents);
-}
-
-bool ColorBuffer::glOpReplaceContents(size_t numBytes, const void* contents) {
-    if (mColorBufferGl) {
-        return mColorBufferGl->replaceContents(contents, numBytes);
-    }
-    return false;
-}
-
-bool ColorBuffer::glOpIsFastBlitSupported() const {
-    if (!mColorBufferGl) {
-        GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
-    }
-
-    return mColorBufferGl->isFastBlitSupported();
-}
-
-void ColorBuffer::glOpPostLayer(const ComposeLayer& l, int frameWidth, int frameHeight) {
-    if (!mColorBufferGl) {
-        GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
-    }
-
-    mColorBufferGl->postLayer(l, frameWidth, frameHeight);
-}
-
-void ColorBuffer::glOpPostViewportScaledWithOverlay(float rotation, float dx, float dy) {
-    if (!mColorBufferGl) {
-        GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "ColorBufferGl not available.";
-    }
-
-    mColorBufferGl->postViewportScaledWithOverlay(rotation, dx, dy);
-}
diff --git a/stream-servers/ColorBuffer.h b/stream-servers/ColorBuffer.h
deleted file mode 100644
index 09c7f6c..0000000
--- a/stream-servers/ColorBuffer.h
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright 2022 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either expresso or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma once
-
-#include <memory>
-
-#include "BorrowedImage.h"
-#include "FrameworkFormats.h"
-#include "Handle.h"
-#include "Hwc2.h"
-#include "aemu/base/files/Stream.h"
-#include "gl/ColorBufferGl.h"
-#include "render-utils/Renderer.h"
-#include "snapshot/LazySnapshotObj.h"
-
-namespace gfxstream {
-class EmulationGl;
-}  // namespace gfxstream
-namespace goldfish_vk {
-struct VkEmulation;
-}  // namespace goldfish_vk
-
-class ColorBuffer : public android::snapshot::LazySnapshotObj<ColorBuffer> {
-   public:
-    static std::shared_ptr<ColorBuffer> create(gfxstream::EmulationGl* emulationGl,
-                                               goldfish_vk::VkEmulation* emulationVk,
-                                               uint32_t width, uint32_t height, GLenum format,
-                                               FrameworkFormat frameworkFormat, HandleType handle);
-
-    static std::shared_ptr<ColorBuffer> onLoad(gfxstream::EmulationGl* emulationGl,
-                                               goldfish_vk::VkEmulation* emulationVk,
-                                               android::base::Stream* stream);
-    void onSave(android::base::Stream* stream);
-    void restore();
-
-    HandleType getHndl() const { return mHandle; }
-    uint32_t getWidth() const { return mWidth; }
-    uint32_t getHeight() const { return mHeight; }
-    GLenum getFormat() const { return mFormat; }
-    FrameworkFormat getFrameworkFormat() const { return mFrameworkFormat; }
-
-    void readToBytes(int x, int y, int width, int height, GLenum pixelsFormat, GLenum pixelsType,
-                     void* outPixels);
-    void readToBytesScaled(int pixelsWidth, int pixelsHeight, GLenum pixelsFormat,
-                           GLenum pixelsType, int pixelsRotation, emugl::Rect rect,
-                           void* outPixels);
-    void readYuvToBytes(int x, int y, int width, int height, void* outPixels, uint32_t pixelsSize);
-
-    bool updateFromBytes(int x, int y, int width, int height, GLenum pixelsFormat,
-                         GLenum pixelsType, const void* pixels);
-    bool updateFromBytes(int x, int y, int width, int height, FrameworkFormat frameworkFormat,
-                         GLenum pixelsFormat, GLenum pixelsType, const void* pixels);
-    bool updateGlFromBytes(const void* bytes, std::size_t bytesSize);
-
-    enum class UsedApi {
-        kGl,
-        kVk,
-    };
-    std::unique_ptr<BorrowedImageInfo> borrowForComposition(UsedApi api, bool isTarget);
-    std::unique_ptr<BorrowedImageInfo> borrowForDisplay(UsedApi api);
-
-    void updateFromGl();
-    void updateFromVk();
-
-    GLuint glOpGetTexture();
-    bool glOpBlitFromCurrentReadBuffer();
-    bool glOpBindToTexture();
-    bool glOpBindToTexture2();
-    bool glOpBindToRenderbuffer();
-    void glOpReadback(unsigned char* img, bool readbackBgra);
-    void glOpReadbackAsync(GLuint buffer, bool readbackBgra);
-    bool glOpImportEglImage(void* image, bool preserveContent);
-    bool glOpImportEglNativePixmap(void* pixmap, bool preserveContent);
-    void glOpSwapYuvTexturesAndUpdate(GLenum format, GLenum type, FrameworkFormat frameworkFormat,
-                                      GLuint* textures);
-    bool glOpReadContents(size_t* outNumBytes, void* outContents);
-    bool glOpReplaceContents(size_t numBytes, const void* contents);
-    bool glOpIsFastBlitSupported() const;
-    void glOpPostLayer(const ComposeLayer& l, int frameWidth, int frameHeight);
-    void glOpPostViewportScaledWithOverlay(float rotation, float dx, float dy);
-
-   private:
-    ColorBuffer(HandleType, uint32_t width, uint32_t height, GLenum format,
-                FrameworkFormat frameworkFormat);
-
-    const HandleType mHandle;
-    const uint32_t mWidth;
-    const uint32_t mHeight;
-    const GLenum mFormat;
-    const FrameworkFormat mFrameworkFormat;
-
-    // If GL emulation is enabled.
-    std::unique_ptr<gfxstream::ColorBufferGl> mColorBufferGl;
-
-    // If Vk emulation is enabled.
-    struct ColorBufferVk {
-        bool thisIsAPlaceHolder = true;
-    };
-    std::unique_ptr<ColorBufferVk> mColorBufferVk;
-
-    bool mGlAndVkAreSharingExternalMemory = false;
-};
-
-typedef std::shared_ptr<ColorBuffer> ColorBufferPtr;
-
-struct ColorBufferRef {
-    ColorBufferPtr cb;
-    uint32_t refcount;  // number of client-side references
-
-    // Tracks whether opened at least once. In O+,
-    // color buffers can be created/closed immediately,
-    // but then registered (opened) afterwards.
-    bool opened;
-
-    // Tracks the time when this buffer got a close request while not being
-    // opened yet.
-    uint64_t closedTs;
-};
-
-typedef std::unordered_map<HandleType, ColorBufferRef> ColorBufferMap;
-typedef std::unordered_multiset<HandleType> ColorBufferSet;
\ No newline at end of file
diff --git a/stream-servers/FrameBuffer.cpp b/stream-servers/FrameBuffer.cpp
index 2126e62..04b344f 100644
--- a/stream-servers/FrameBuffer.cpp
+++ b/stream-servers/FrameBuffer.cpp
@@ -229,7 +229,6 @@
         if (!vkEmu) {
             ERR("Failed to initialize global Vulkan emulation. Disable the Vulkan support.");
         }
-        fb->m_emulationVk = vkEmu;
     }
     if (vkEmu) {
         fb->m_vulkanEnabled = true;
@@ -444,6 +443,22 @@
     }
 }
 
+void FrameBuffer::setColorBufferInUse(
+    uint32_t colorBufferHandle,
+    bool inUse) {
+
+    AutoLock mutex(m_lock);
+
+    ColorBufferPtr colorBuffer = findColorBuffer(colorBufferHandle);
+    if (!colorBuffer) {
+        // bad colorbuffer handle
+        ERR("FB: setColorBufferInUse cb handle %#x not found", colorBufferHandle);
+        return;
+    }
+
+    colorBuffer->setInUse(inUse);
+}
+
 void FrameBuffer::fillGLESUsages(android_studio::EmulatorGLESUsages* usages) {
     if (s_egl.eglFillUsages) {
         s_egl.eglFillUsages(usages);
@@ -647,10 +662,11 @@
     res.wait();
     if (postOnlyOnMainThread && (PostCmd::Screenshot == post.cmd) &&
         emugl::get_emugl_window_operations().isRunningInUiThread()) {
-        post.cb->readToBytesScaled(post.screenshot.screenwidth, post.screenshot.screenheight,
-                                   post.screenshot.format, post.screenshot.type,
-                                   post.screenshot.rotation, post.screenshot.rect,
-                                   post.screenshot.pixels);
+        post.cb->readPixelsScaled(post.screenshot.screenwidth,
+                                  post.screenshot.screenheight,
+                                  post.screenshot.format, post.screenshot.type,
+                                  post.screenshot.rotation,
+                                  post.screenshot.pixels, post.screenshot.rect);
     } else {
         std::future<void> completeFuture =
             m_postThread.enqueue(Post(std::move(post)));
@@ -1055,8 +1071,21 @@
         GLenum p_internalFormat,
         FrameworkFormat p_frameworkFormat,
         HandleType handle) {
-    ColorBufferPtr cb = ColorBuffer::create(m_emulationGl.get(), m_emulationVk, p_width, p_height,
-                                            p_internalFormat, p_frameworkFormat, handle);
+    EGLDisplay display = EGL_NO_DISPLAY;
+    ContextHelper* contextHelper = nullptr;
+    TextureDraw* textureDraw = nullptr;
+    bool isFastBlitSupported = false;
+    if (m_emulationGl) {
+        display = getDisplay();
+        contextHelper = getPbufferSurfaceContextHelper();
+        textureDraw = getTextureDraw();
+        isFastBlitSupported = this->isFastBlitSupported();
+    }
+
+    ColorBufferPtr cb(ColorBuffer::create(display, p_width, p_height,
+                                          p_internalFormat, p_frameworkFormat,
+                                          handle, contextHelper, textureDraw,
+                                          isFastBlitSupported, m_guestUsesAngle));
     if (cb.get() == nullptr) {
         GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
             << "Failed to create ColorBuffer:" << handle << " format:" << p_internalFormat
@@ -1064,6 +1093,27 @@
             << " height:" << p_height;
     }
 
+    if (m_vulkanEnabled) {
+        if (!goldfish_vk::setupVkColorBuffer(
+                p_width, p_height, p_internalFormat, p_frameworkFormat, handle,
+                /*vulkanOnly=*/m_guestUsesAngle, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) {
+            GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
+                << "Failed to create color buffer."
+                << " format:" << p_internalFormat << " width:" << p_width << " height:" << p_height;
+            return 0;
+        }
+
+        auto memoryExport = goldfish_vk::exportColorBufferMemory(handle);
+        if (memoryExport) {
+            if (!cb->importMemory(std::move(memoryExport->descriptor), memoryExport->size,
+                                  /*dedicated=*/false, memoryExport->linearTiling,
+                                  /*vulkanOnly*/false)) {
+                ERR("Failed to import memory to ColorBuffer:%d", handle);
+                return 0;
+            }
+        }
+    }
+
     assert(m_colorbuffers.count(handle) == 0);
     // When guest feature flag RefCountPipe is on, no reference counting is
     // needed. We only memoize the mapping from handle to ColorBuffer.
@@ -1862,8 +1912,19 @@
     buffer->read(offset, size, bytes);
 }
 
-void FrameBuffer::readColorBuffer(HandleType p_colorbuffer, int x, int y, int width, int height,
-                                  GLenum format, GLenum type, void* pixels) {
+void FrameBuffer::readColorBuffer(HandleType p_colorbuffer,
+                                  int x,
+                                  int y,
+                                  int width,
+                                  int height,
+                                  GLenum format,
+                                  GLenum type,
+                                  void* pixels) {
+    if (m_guestUsesAngle) {
+        goldfish_vk::readColorBufferToBytes(p_colorbuffer, x, y, width, height, pixels);
+        return;
+    }
+
     AutoLock mutex(m_lock);
 
     ColorBufferPtr colorBuffer = findColorBuffer(p_colorbuffer);
@@ -1872,11 +1933,21 @@
         return;
     }
 
-    colorBuffer->readToBytes(x, y, width, height, format, type, pixels);
+    colorBuffer->readPixels(x, y, width, height, format, type, pixels);
 }
 
-void FrameBuffer::readColorBufferYUV(HandleType p_colorbuffer, int x, int y, int width, int height,
-                                     void* pixels, uint32_t pixels_size) {
+void FrameBuffer::readColorBufferYUV(HandleType p_colorbuffer,
+                                     int x,
+                                     int y,
+                                     int width,
+                                     int height,
+                                     void* pixels,
+                                     uint32_t pixels_size) {
+    if (m_guestUsesAngle) {
+        goldfish_vk::readColorBufferToBytes(p_colorbuffer, x, y, width, height, pixels);
+        return;
+    }
+
     AutoLock mutex(m_lock);
 
     ColorBufferPtr colorBuffer = findColorBuffer(p_colorbuffer);
@@ -1885,7 +1956,7 @@
         return;
     }
 
-    colorBuffer->readYuvToBytes(x, y, width, height, pixels, pixels_size);
+    colorBuffer->readPixelsYUVCached(x, y, width, height, pixels, pixels_size);
 }
 
 void FrameBuffer::createYUVTextures(uint32_t type,
@@ -1969,9 +2040,11 @@
             // bad colorbuffer handle
             return;
         }
-        colorBuffer->glOpSwapYuvTexturesAndUpdate(
-            format, type, static_cast<FrameworkFormat>(texture_type), textures);
+        colorBuffer->swapYUVTextures(texture_type, textures);
     }
+
+    updateColorBuffer(p_colorbuffer, x, y, width, height, format, type,
+                      nullptr);
 }
 
 bool FrameBuffer::updateBuffer(HandleType p_buffer, uint64_t offset, uint64_t size, void* bytes) {
@@ -2004,6 +2077,10 @@
         return false;
     }
 
+    if (m_guestUsesAngle) {
+        return goldfish_vk::updateColorBufferFromBytes(p_colorbuffer, x, y, width, height, pixels);
+    }
+
     AutoLock mutex(m_lock);
 
     ColorBufferPtr colorBuffer = findColorBuffer(p_colorbuffer);
@@ -2012,7 +2089,7 @@
         return false;
     }
 
-    colorBuffer->updateFromBytes(x, y, width, height, format, type, pixels);
+    colorBuffer->subUpdate(x, y, width, height, format, type, pixels);
 
     return true;
 }
@@ -2033,7 +2110,9 @@
         return false;
     }
 
-    (*c).second.cb->updateFromBytes(x, y, width, height, fwkFormat, format, type, pixels);
+    (*c).second.cb->subUpdateFromFrameworkFormat(x, y, width, height, fwkFormat, format, type,
+                                                 pixels);
+
     return true;
 }
 
@@ -2047,7 +2126,7 @@
         return false;
     }
 
-    return colorBuffer->glOpReplaceContents(numBytes, pixels);
+    return colorBuffer->replaceContents(pixels, numBytes);
 }
 
 bool FrameBuffer::readColorBufferContents(
@@ -2061,7 +2140,7 @@
         return false;
     }
 
-    return colorBuffer->glOpReadContents(numBytes, pixels);
+    return colorBuffer->readContents(numBytes, pixels);
 }
 
 bool FrameBuffer::getColorBufferInfo(
@@ -2078,7 +2157,7 @@
 
     *width = colorBuffer->getWidth();
     *height = colorBuffer->getHeight();
-    *internalformat = colorBuffer->getFormat();
+    *internalformat = colorBuffer->getInternalFormat();
     if (frameworkFormat) {
         *frameworkFormat = colorBuffer->getFrameworkFormat();
     }
@@ -2109,7 +2188,7 @@
         return false;
     }
 
-    return colorBuffer->glOpBindToTexture();
+    return colorBuffer->bindToTexture();
 }
 
 bool FrameBuffer::bindColorBufferToTexture2(HandleType p_colorbuffer) {
@@ -2126,7 +2205,7 @@
         return false;
     }
 
-    return colorBuffer->glOpBindToTexture2();
+    return colorBuffer->bindToTexture2();
 }
 
 bool FrameBuffer::bindColorBufferToRenderbuffer(HandleType p_colorbuffer) {
@@ -2138,7 +2217,7 @@
         return false;
     }
 
-    return colorBuffer->glOpBindToRenderbuffer();
+    return colorBuffer->bindToRenderbuffer();
 }
 
 bool FrameBuffer::bindContext(HandleType p_context,
@@ -2501,7 +2580,7 @@
                 doPostCallback(iter.second.img, iter.first);
             }
         } else {
-            cb->glOpReadback(iter.second.img, iter.second.readBgra);
+            cb->readback(iter.second.img, iter.second.readBgra);
             doPostCallback(iter.second.img, iter.first);
         }
     }
@@ -3115,7 +3194,8 @@
         m_guestManagedColorBufferLifetime = stream->getByte();
         loadCollection(
             stream, &m_colorbuffers, [this, now](Stream* stream) -> ColorBufferMap::value_type {
-                ColorBufferPtr cb = ColorBuffer::onLoad(m_emulationGl.get(), m_emulationVk, stream);
+                ColorBufferPtr cb(ColorBuffer::onLoad(stream, getDisplay(), getPbufferSurfaceContextHelper(),
+                                                      getTextureDraw(), isFastBlitSupported()));
                 const HandleType handle = cb->getHndl();
                 const unsigned refCount = stream->getBe32();
                 const bool opened = stream->getByte();
@@ -3404,9 +3484,9 @@
 
     switch (type) {
         case RESOURCE_TYPE_EGL_NATIVE_PIXMAP:
-            return colorBuffer->glOpImportEglNativePixmap(resource, preserveContent);
+            return colorBuffer->importEglNativePixmap(resource, preserveContent);
         case RESOURCE_TYPE_EGL_IMAGE:
-            return colorBuffer->glOpImportEglImage(resource, preserveContent);
+            return colorBuffer->importEglImage(resource, preserveContent);
         default:
             ERR("Error: unsupported resource type: %u", type);
             return false;
@@ -3451,25 +3531,30 @@
 
 std::unique_ptr<BorrowedImageInfo> FrameBuffer::borrowColorBufferForComposition(
     uint32_t colorBufferHandle, bool colorBufferIsTarget) {
+    if (m_useVulkanComposition) {
+        return goldfish_vk::borrowColorBufferForComposition(colorBufferHandle, colorBufferIsTarget);
+    }
+
     ColorBufferPtr colorBufferPtr = findColorBuffer(colorBufferHandle);
     if (!colorBufferPtr) {
         ERR("Failed to get borrowed image info for ColorBuffer:%d", colorBufferHandle);
         return nullptr;
     }
-
-    const auto api = m_useVulkanComposition ? ColorBuffer::UsedApi::kVk : ColorBuffer::UsedApi::kGl;
-    return colorBufferPtr->borrowForComposition(api, colorBufferIsTarget);
+    return colorBufferPtr->getBorrowedImageInfo();
 }
 
 std::unique_ptr<BorrowedImageInfo> FrameBuffer::borrowColorBufferForDisplay(
-    uint32_t colorBufferHandle) {
+        uint32_t colorBufferHandle) {
+    if (m_useVulkanComposition) {
+        return goldfish_vk::borrowColorBufferForDisplay(colorBufferHandle);
+    }
+
     ColorBufferPtr colorBufferPtr = findColorBuffer(colorBufferHandle);
     if (!colorBufferPtr) {
         ERR("Failed to get borrowed image info for ColorBuffer:%d", colorBufferHandle);
         return nullptr;
     }
-    const auto api = m_useVulkanComposition ? ColorBuffer::UsedApi::kVk : ColorBuffer::UsedApi::kGl;
-    return colorBufferPtr->borrowForDisplay(api);
+    return colorBufferPtr->getBorrowedImageInfo();
 }
 
 gfxstream::EmulationGl& FrameBuffer::getEmulationGl() {
@@ -3685,7 +3770,30 @@
         ERR("Failed to find ColorBuffer:%d", colorBufferHandle);
         return;
     }
-    colorBuffer->updateFromGl();
+
+    // TODO(b/233939967): move the below into the generic ColorBuffer.
+
+    if (!goldfish_vk::colorBufferNeedsUpdateBetweenGlAndVk(colorBufferHandle)) {
+        return;
+    }
+
+    std::size_t contentsSize = 0;
+    if (!colorBuffer->readContents(&contentsSize, nullptr)) {
+        ERR("Failed to get GL contents size for ColorBuffer:%d", colorBufferHandle);
+        return;
+    }
+
+    std::vector<uint8_t> contents(contentsSize, 0);
+
+    if (!colorBuffer->readContents(&contentsSize, contents.data())) {
+        ERR("Failed to get GL contents for ColorBuffer:%d", colorBufferHandle);
+        return;
+    }
+
+    if (!goldfish_vk::updateColorBufferFromBytes(colorBufferHandle, contents)) {
+        ERR("Failed to set VK contents for ColorBuffer:%d", colorBufferHandle);
+        return;
+    }
 }
 
 void FrameBuffer::updateColorBufferFromVk(HandleType colorBufferHandle) {
@@ -3696,5 +3804,23 @@
         ERR("Failed to find ColorBuffer:%d", colorBufferHandle);
         return;
     }
-    colorBuffer->updateFromVk();
+
+    // TODO(b/233939967): move the below into the generic ColorBuffer.
+
+    if (!goldfish_vk::colorBufferNeedsUpdateBetweenGlAndVk(colorBufferHandle)) {
+        return;
+    }
+
+    std::vector<uint8_t> contents;
+    if (!goldfish_vk::readColorBufferToBytes(colorBufferHandle, &contents)) {
+        ERR("Failed to get VK contents for ColorBuffer:%d", colorBufferHandle);
+        return;
+    }
+
+    if (contents.empty()) return;
+
+    if (!colorBuffer->replaceContents(contents.data(), contents.size())) {
+        ERR("Failed to set GL contents for ColorBuffer:%d", colorBufferHandle);
+        return;
+    }
 }
diff --git a/stream-servers/FrameBuffer.h b/stream-servers/FrameBuffer.h
index af83273..3799295 100644
--- a/stream-servers/FrameBuffer.h
+++ b/stream-servers/FrameBuffer.h
@@ -557,6 +557,7 @@
 
     bool isVulkanInteropSupported() const { return m_vulkanInteropSupported; }
     bool isVulkanEnabled() const { return m_vulkanEnabled; }
+    void setColorBufferInUse(uint32_t colorBufferHandle, bool inUse);
 
     // Fill GLES usage protobuf
     void fillGLESUsages(android_studio::EmulatorGLESUsages*);
@@ -854,7 +855,6 @@
     Compositor* m_compositor = nullptr;
     bool m_useVulkanComposition = false;
 
-    goldfish_vk::VkEmulation* m_emulationVk = nullptr;
     // The implementation for Vulkan native swapchain. Only initialized when useVulkan is set when
     // calling FrameBuffer::initialize(). DisplayVk is actually owned by VkEmulation.
     DisplayVk *m_displayVk = nullptr;
diff --git a/stream-servers/PostWorker.cpp b/stream-servers/PostWorker.cpp
index 213cd96..58bc5ec 100644
--- a/stream-servers/PostWorker.cpp
+++ b/stream-servers/PostWorker.cpp
@@ -19,10 +19,10 @@
 
 #include <chrono>
 
-#include "ColorBuffer.h"
 #include "FrameBuffer.h"
 #include "RenderThreadInfo.h"
 #include "aemu/base/Tracing.h"
+#include "gl/ColorBufferGl.h"
 #include "gl/DisplayGl.h"
 #include "host-common/GfxstreamFatalError.h"
 #include "host-common/logging.h"
@@ -332,7 +332,8 @@
         GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) <<
                             "Screenshot not supported with native Vulkan swapchain enabled.";
     }
-    cb->readToBytesScaled(width, height, format, type, rotation, rect, pixels);
+    cb->readPixelsScaled(
+        width, height, format, type, rotation, pixels, rect);
 }
 
 void PostWorker::block(std::promise<void> scheduledSignal, std::future<void> continueSignal) {
diff --git a/stream-servers/gl/ColorBufferGl.cpp b/stream-servers/gl/ColorBufferGl.cpp
index e12cab9..dc81841 100644
--- a/stream-servers/gl/ColorBufferGl.cpp
+++ b/stream-servers/gl/ColorBufferGl.cpp
@@ -40,7 +40,6 @@
 using gfxstream::GLESApi_CM;
 using gfxstream::GLESApi_2;
 
-namespace gfxstream {
 namespace {
 
 // Lazily create and bind a framebuffer object to the current host context.
@@ -66,7 +65,7 @@
 #if DEBUG_CB_FBO
     GLenum status = s_gles2.glCheckFramebufferStatus(GL_FRAMEBUFFER);
     if (status != GL_FRAMEBUFFER_COMPLETE_OES) {
-        ERR("ColorBufferGl::bindFbo: FBO not complete: %#x\n", status);
+        ERR("ColorBuffer::bindFbo: FBO not complete: %#x\n", status);
         s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, 0);
         s_gles2.glDeleteFramebuffers(1, fbo);
         *fbo = 0;
@@ -212,12 +211,16 @@
 }
 
 // static
-std::unique_ptr<ColorBufferGl> ColorBufferGl::create(EGLDisplay p_display, int p_width,
-                                                     int p_height, GLint p_internalFormat,
-                                                     FrameworkFormat p_frameworkFormat,
-                                                     HandleType hndl, ContextHelper* helper,
-                                                     TextureDraw* textureDraw,
-                                                     bool fastBlitSupported) {
+ColorBuffer* ColorBuffer::create(EGLDisplay p_display,
+                                 int p_width,
+                                 int p_height,
+                                 GLint p_internalFormat,
+                                 FrameworkFormat p_frameworkFormat,
+                                 HandleType hndl,
+                                 ContextHelper* helper,
+                                 TextureDraw* textureDraw,
+                                 bool fastBlitSupported,
+                                 bool vulkanOnly) {
     GLenum texFormat = 0;
     GLenum pixelType = GL_UNSIGNED_BYTE;
     int bytesPerPixel = 4;
@@ -227,15 +230,17 @@
     if (!sGetFormatParameters(&p_internalFormat, &texFormat, &pixelType,
                               &bytesPerPixel, &p_sizedInternalFormat,
                               &isBlob)) {
-        fprintf(stderr, "ColorBufferGl::create invalid format 0x%x\n", p_internalFormat);
-        return nullptr;
+        fprintf(stderr, "ColorBuffer::create invalid format 0x%x\n",
+                p_internalFormat);
+        return NULL;
     }
     const unsigned long bufsize = ((unsigned long)bytesPerPixel) * p_width
             * p_height;
 
     // This constructor is private, so std::make_unique can't be used.
-    std::unique_ptr<ColorBufferGl> cb{
-        new ColorBufferGl(p_display, hndl, p_width, p_height, helper, textureDraw)};
+    std::unique_ptr<ColorBuffer> cb{new ColorBuffer(p_display, hndl, helper, textureDraw)};
+    cb->m_width = p_width;
+    cb->m_height = p_height;
     cb->m_internalFormat = p_internalFormat;
     cb->m_sizedInternalFormat = p_sizedInternalFormat;
     cb->m_format = texFormat;
@@ -243,13 +248,18 @@
     cb->m_frameworkFormat = p_frameworkFormat;
     cb->m_fastBlitSupported = fastBlitSupported;
     cb->m_numBytes = (size_t)bufsize;
+    cb->m_vulkanOnly = vulkanOnly;
+
+    if (vulkanOnly) {
+        return cb.release();
+    }
 
     RecursiveScopedContextBind context(helper);
     if (!context.isOk()) {
-        return nullptr;
+        return NULL;
     }
 
-    GL_SCOPED_DEBUG_GROUP("ColorBufferGl::create(handle:%d)", hndl);
+    GL_SCOPED_DEBUG_GROUP("ColorBuffer::create(handle:%d)", hndl);
 
     GLint prevUnpackAlignment;
     s_gles2.glGetIntegerv(GL_UNPACK_ALIGNMENT, &prevUnpackAlignment);
@@ -320,19 +330,18 @@
     s_gles2.glPixelStorei(GL_UNPACK_ALIGNMENT, prevUnpackAlignment);
 
     s_gles2.glFinish();
-    return cb;
+    return cb.release();
 }
 
-ColorBufferGl::ColorBufferGl(EGLDisplay display, HandleType hndl, GLuint width, GLuint height,
-                             ContextHelper* helper, TextureDraw* textureDraw)
-    : m_width(width),
-      m_height(height),
-      m_display(display),
-      m_helper(helper),
-      m_textureDraw(textureDraw),
-      mHndl(hndl) {}
+ColorBuffer::ColorBuffer(EGLDisplay display, HandleType hndl, ContextHelper* helper,
+                         TextureDraw* textureDraw)
+    : m_display(display), m_helper(helper), m_textureDraw(textureDraw), mHndl(hndl) {}
 
-ColorBufferGl::~ColorBufferGl() {
+ColorBuffer::~ColorBuffer() {
+    if (m_vulkanOnly) {
+        return;
+    }
+
     RecursiveScopedContextBind context(m_helper);
 
     if (m_blitEGLImage) {
@@ -366,18 +375,22 @@
     delete m_resizer;
 }
 
-void ColorBufferGl::readPixels(int x, int y, int width, int height, GLenum p_format, GLenum p_type,
-                               void* pixels) {
+void ColorBuffer::readPixels(int x,
+                             int y,
+                             int width,
+                             int height,
+                             GLenum p_format,
+                             GLenum p_type,
+                             void* pixels) {
     RecursiveScopedContextBind context(m_helper);
     if (!context.isOk()) {
         return;
     }
 
-    GL_SCOPED_DEBUG_GROUP("ColorBufferGl::readPixels(handle:%d fbo:%d tex:%d)", mHndl, m_fbo,
-                          m_tex);
+    GL_SCOPED_DEBUG_GROUP("ColorBuffer::readPixels(handle:%d fbo:%d tex:%d)", mHndl, m_fbo, m_tex);
 
     p_format = sGetUnsizedColorBufferFormat(p_format);
-
+    touch();
     waitSync();
 
     if (bindFbo(&m_fbo, m_tex, m_needFboReattach)) {
@@ -391,8 +404,8 @@
     }
 }
 
-void ColorBufferGl::readPixelsScaled(int width, int height, GLenum p_format, GLenum p_type,
-                                     int rotation, emugl::Rect rect, void* pixels) {
+void ColorBuffer::readPixelsScaled(int width, int height, GLenum p_format, GLenum p_type,
+                                   int rotation, void* pixels, emugl::Rect rect) {
     RecursiveScopedContextBind context(m_helper);
     if (!context.isOk()) {
         return;
@@ -408,7 +421,7 @@
         return;
     }
     p_format = sGetUnsizedColorBufferFormat(p_format);
-
+    touch();
     waitSync();
     GLuint tex = m_resizer->update(m_tex, width, height, rotation);
     if (bindFbo(&m_scaleRotationFbo, tex, m_needFboReattach)) {
@@ -453,13 +466,18 @@
     }
 }
 
-void ColorBufferGl::readPixelsYUVCached(int x, int y, int width, int height, void* pixels,
-                                        uint32_t pixels_size) {
+void ColorBuffer::readPixelsYUVCached(int x,
+                                      int y,
+                                      int width,
+                                      int height,
+                                      void* pixels,
+                                      uint32_t pixels_size) {
     RecursiveScopedContextBind context(m_helper);
     if (!context.isOk()) {
         return;
     }
 
+    touch();
     waitSync();
 
 #if DEBUG_CB_FBO
@@ -469,12 +487,17 @@
     assert(m_yuv_converter.get());
 #endif
 
-    m_yuv_converter->readPixels((uint8_t*)pixels, pixels_size);
+    if (!m_vulkanOnly) {
+        m_yuv_converter->readPixels((uint8_t*)pixels, pixels_size);
+    } else {
+        GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) <<
+                        "Unexpected function call when m_vulkanOnly";
+    }
 
     return;
 }
 
-void ColorBufferGl::reformat(GLint internalformat, GLenum type) {
+void ColorBuffer::reformat(GLint internalformat, GLenum type) {
     GLenum texFormat = internalformat;
     GLenum pixelType = GL_UNSIGNED_BYTE;
     GLint sizedInternalFormat = GL_RGBA8;
@@ -536,9 +559,14 @@
     m_numBytes = bpp * m_width * m_height;
 }
 
-void ColorBufferGl::swapYUVTextures(FrameworkFormat type, uint32_t* textures) {
-    if (type == FrameworkFormat::FRAMEWORK_FORMAT_NV12) {
-        m_yuv_converter->swapTextures(type, textures);
+void ColorBuffer::swapYUVTextures(uint32_t type, uint32_t* textures) {
+    if (type == FRAMEWORK_FORMAT_NV12) {
+        if (!m_vulkanOnly) {
+            m_yuv_converter->swapTextures(type, textures);
+        } else {
+            GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
+                << "Unexpected function call when m_vulkanOnly";
+        }
     } else {
         fprintf(stderr,
                 "%s: ERROR: format other than NV12 is not supported: 0x%x\n",
@@ -546,21 +574,31 @@
     }
 }
 
-void ColorBufferGl::subUpdate(int x, int y, int width, int height, GLenum p_format, GLenum p_type,
-                              const void* pixels) {
+void ColorBuffer::subUpdate(int x,
+                            int y,
+                            int width,
+                            int height,
+                            GLenum p_format,
+                            GLenum p_type,
+                            void* pixels) {
+    if (m_vulkanOnly) {
+        return;
+    }
     subUpdateFromFrameworkFormat(x, y, width, height, m_frameworkFormat, p_format, p_type, pixels);
 }
 
-void ColorBufferGl::subUpdateFromFrameworkFormat(int x, int y, int width, int height,
-                                                 FrameworkFormat fwkFormat, GLenum p_format,
-                                                 GLenum p_type, const void* pixels) {
+void ColorBuffer::subUpdateFromFrameworkFormat(int x, int y, int width, int height,
+                                               FrameworkFormat fwkFormat, GLenum p_format,
+                                               GLenum p_type, void* pixels) {
     const GLenum p_unsizedFormat = sGetUnsizedColorBufferFormat(p_format);
     RecursiveScopedContextBind context(m_helper);
     if (!context.isOk()) {
         return;
     }
 
-    GL_SCOPED_DEBUG_GROUP("ColorBufferGl::subUpdate(handle:%d fbo:%d tex:%d)", mHndl, m_fbo, m_tex);
+    GL_SCOPED_DEBUG_GROUP("ColorBuffer::subUpdate(handle:%d fbo:%d tex:%d)", mHndl, m_fbo, m_tex);
+
+    touch();
 
     if (m_needFormatCheck) {
         if (p_type != m_type || p_format != m_format) {
@@ -595,7 +633,7 @@
     }
 }
 
-bool ColorBufferGl::replaceContents(const void* newContents, size_t numBytes) {
+bool ColorBuffer::replaceContents(const void* newContents, size_t numBytes) {
     if (m_vulkanOnly) {
         return false;
     }
@@ -618,6 +656,8 @@
         return false;
     }
 
+    touch();
+
     s_gles2.glBindTexture(GL_TEXTURE_2D, m_tex);
     s_gles2.glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
     s_gles2.glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_width, m_height, m_format,
@@ -631,7 +671,7 @@
     return true;
 }
 
-bool ColorBufferGl::readContents(size_t* numBytes, void* pixels) {
+bool ColorBuffer::readContents(size_t* numBytes, void* pixels) {
     if (m_yuv_converter) {
         // common code path for vk & gles
         *numBytes = m_yuv_converter->getDataSize();
@@ -651,7 +691,7 @@
     }
 }
 
-bool ColorBufferGl::blitFromCurrentReadBuffer() {
+bool ColorBuffer::blitFromCurrentReadBuffer() {
     RenderThreadInfoGl* const tInfo = RenderThreadInfoGl::get();
     if (!tInfo) {
         GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
@@ -663,6 +703,8 @@
         return false;
     }
 
+    touch();
+
     if (m_fastBlitSupported) {
         s_egl.eglBlitFromCurrentReadBufferANDROID(m_display, m_eglImage);
         m_sync = (GLsync)s_egl.eglSetImageFenceANDROID(m_display, m_eglImage);
@@ -813,7 +855,7 @@
     return true;
 }
 
-bool ColorBufferGl::bindToTexture() {
+bool ColorBuffer::bindToTexture() {
     if (!m_eglImage) {
         return false;
     }
@@ -827,6 +869,7 @@
     if (!tInfo->currContext.get()) {
         return false;
     }
+    touch();
 
     if (tInfo->currContext->clientVersion() > GLESApi_CM) {
         s_gles2.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_eglImage);
@@ -836,7 +879,7 @@
     return true;
 }
 
-bool ColorBufferGl::bindToTexture2() {
+bool ColorBuffer::bindToTexture2() {
     if (!m_eglImage) {
         return false;
     }
@@ -845,7 +888,7 @@
     return true;
 }
 
-bool ColorBufferGl::bindToRenderbuffer() {
+bool ColorBuffer::bindToRenderbuffer() {
     if (!m_eglImage) {
         return false;
     }
@@ -859,7 +902,7 @@
     if (!tInfo->currContext.get()) {
         return false;
     }
-
+    touch();
     if (tInfo->currContext->clientVersion() > GLESApi_CM) {
         s_gles2.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER_OES,
                                                        m_eglImage);
@@ -870,38 +913,40 @@
     return true;
 }
 
-GLuint ColorBufferGl::getViewportScaledTexture() { return m_resizer->update(m_tex); }
+GLuint ColorBuffer::getViewportScaledTexture() {
+    return m_resizer->update(m_tex);
+}
 
-void ColorBufferGl::setSync(bool debug) {
+void ColorBuffer::setSync(bool debug) {
     m_sync = (GLsync)s_egl.eglSetImageFenceANDROID(m_display, m_eglImage);
     if (debug) fprintf(stderr, "%s: %u to %p\n", __func__, getHndl(), m_sync);
 }
 
-void ColorBufferGl::waitSync(bool debug) {
+void ColorBuffer::waitSync(bool debug) {
     if (debug) fprintf(stderr, "%s: %u sync %p\n", __func__, getHndl(), m_sync);
     if (m_sync) {
         s_egl.eglWaitImageFenceANDROID(m_display, m_sync);
     }
 }
 
-bool ColorBufferGl::post(GLuint tex, float rotation, float dx, float dy) {
+bool ColorBuffer::post(GLuint tex, float rotation, float dx, float dy) {
     // NOTE: Do not call m_helper->setupContext() here!
     waitSync();
     return m_textureDraw->draw(tex, rotation, dx, dy);
 }
 
-bool ColorBufferGl::postViewportScaledWithOverlay(float rotation, float dx, float dy) {
+bool ColorBuffer::postWithOverlay(float rotation, float dx, float dy) {
     // NOTE: Do not call m_helper->setupContext() here!
     waitSync();
     return m_textureDraw->drawWithOverlay(getViewportScaledTexture(), rotation, dx, dy);
 }
 
-void ColorBufferGl::readback(unsigned char* img, bool readbackBgra) {
+void ColorBuffer::readback(unsigned char* img, bool readbackBgra) {
     RecursiveScopedContextBind context(m_helper);
     if (!context.isOk()) {
         return;
     }
-
+    touch();
     waitSync();
 
     if (bindFbo(&m_fbo, m_tex, m_needFboReattach)) {
@@ -915,12 +960,12 @@
     }
 }
 
-void ColorBufferGl::readbackAsync(GLuint buffer, bool readbackBgra) {
+void ColorBuffer::readbackAsync(GLuint buffer, bool readbackBgra) {
     RecursiveScopedContextBind context(m_helper);
     if (!context.isOk()) {
         return;
     }
-
+    touch();
     waitSync();
 
     if (bindFbo(&m_fbo, m_tex, m_needFboReattach)) {
@@ -934,9 +979,11 @@
     }
 }
 
-HandleType ColorBufferGl::getHndl() const { return mHndl; }
+HandleType ColorBuffer::getHndl() const {
+    return mHndl;
+}
 
-void ColorBufferGl::onSave(android::base::Stream* stream) {
+void ColorBuffer::onSave(android::base::Stream* stream) {
     stream->putBe32(getHndl());
     stream->putBe32(static_cast<uint32_t>(m_width));
     stream->putBe32(static_cast<uint32_t>(m_height));
@@ -949,10 +996,11 @@
     stream->putBe32(m_needFormatCheck);
 }
 
-std::unique_ptr<ColorBufferGl> ColorBufferGl::onLoad(android::base::Stream* stream,
-                                                     EGLDisplay p_display, ContextHelper* helper,
-                                                     TextureDraw* textureDraw,
-                                                     bool fastBlitSupported) {
+ColorBuffer* ColorBuffer::onLoad(android::base::Stream* stream,
+                                 EGLDisplay p_display,
+                                 ContextHelper* helper,
+                                 TextureDraw* textureDraw,
+                                 bool fastBlitSupported) {
     HandleType hndl = static_cast<HandleType>(stream->getBe32());
     GLuint width = static_cast<GLuint>(stream->getBe32());
     GLuint height = static_cast<GLuint>(stream->getBe32());
@@ -967,11 +1015,13 @@
         return create(p_display, width, height, internalFormat, frameworkFormat,
                       hndl, helper, textureDraw, fastBlitSupported);
     }
-    std::unique_ptr<ColorBufferGl> cb(
-        new ColorBufferGl(p_display, hndl, width, height, helper, textureDraw));
+    ColorBuffer* cb = new ColorBuffer(p_display, hndl, helper, textureDraw);
+    cb->mNeedRestore = true;
     cb->m_eglImage = eglImage;
     cb->m_blitEGLImage = blitEGLImage;
     assert(eglImage && blitEGLImage);
+    cb->m_width = width;
+    cb->m_height = height;
     cb->m_internalFormat = internalFormat;
     cb->m_frameworkFormat = frameworkFormat;
     cb->m_fastBlitSupported = fastBlitSupported;
@@ -979,7 +1029,7 @@
     return cb;
 }
 
-void ColorBufferGl::restore() {
+void ColorBuffer::restore() {
     RecursiveScopedContextBind context(m_helper);
     s_gles2.glGenTextures(1, &m_tex);
     s_gles2.glBindTexture(GL_TEXTURE_2D, m_tex);
@@ -1000,16 +1050,24 @@
     }
 }
 
-GLuint ColorBufferGl::getTexture() { return m_tex; }
 
-void ColorBufferGl::postLayer(const ComposeLayer& l, int frameWidth, int frameHeight) {
+GLuint ColorBuffer::getTexture() {
+    touch();
+    return m_tex;
+}
+
+void ColorBuffer::postLayer(const ComposeLayer& l, int frameWidth, int frameHeight) {
+    if (m_inUse) fprintf(stderr, "%s: cb in use\n", __func__);
     waitSync();
     m_textureDraw->drawLayer(l, frameWidth, frameHeight, m_width, m_height,
                              getViewportScaledTexture());
 }
 
-bool ColorBufferGl::importMemory(ManagedDescriptor externalDescriptor, uint64_t size,
-                                 bool dedicated, bool linearTiling) {
+bool ColorBuffer::importMemory(ManagedDescriptor externalDescriptor, uint64_t size, bool dedicated,
+                               bool linearTiling, bool vulkanOnly) {
+    if (m_vulkanOnly) {
+        return true;
+    }
     RecursiveScopedContextBind context(m_helper);
     s_gles2.glCreateMemoryObjectsEXT(1, &m_memoryObject);
     if (dedicated) {
@@ -1054,10 +1112,12 @@
 
     std::vector<uint8_t> prevContents;
 
-    size_t bytes;
-    readContents(&bytes, nullptr);
-    prevContents.resize(bytes, 0);
-    readContents(&bytes, prevContents.data());
+    if (!vulkanOnly) {
+        size_t bytes;
+        readContents(&bytes, nullptr);
+        prevContents.resize(bytes, 0);
+        readContents(&bytes, prevContents.data());
+    }
 
     s_gles2.glDeleteTextures(1, &m_tex);
     s_gles2.glGenTextures(1, &m_tex);
@@ -1091,12 +1151,15 @@
             m_display, s_egl.eglGetCurrentContext(), EGL_GL_TEXTURE_2D_KHR,
             (EGLClientBuffer)SafePointerFromUInt(m_tex), NULL);
 
-    replaceContents(prevContents.data(), m_numBytes);
+    if (!vulkanOnly) {
+        replaceContents(prevContents.data(), m_numBytes);
+    }
 
     return true;
 }
 
-bool ColorBufferGl::importEglNativePixmap(void* pixmap, bool preserveContent) {
+bool ColorBuffer::importEglNativePixmap(void* pixmap, bool preserveContent) {
+
     EGLImageKHR image = s_egl.eglCreateImageKHR(m_display, EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR, pixmap, nullptr);
 
     if (image == EGL_NO_IMAGE_KHR) {
@@ -1104,7 +1167,7 @@
         return false;
     }
 
-    // Assume pixmap is compatible with ColorBufferGl's current dimensions and internal format.
+    // Assume pixmap is compatible with ColorBuffer's current dimensions and internal format.
     EGLBoolean setInfoRes = s_egl.eglSetImageInfoANDROID(m_display, image, m_width, m_height, m_internalFormat);
 
     if (EGL_TRUE != setInfoRes) {
@@ -1117,13 +1180,12 @@
     return true;
 }
 
-bool ColorBufferGl::importEglImage(void* nativeEglImage, bool preserveContent) {
+bool ColorBuffer::importEglImage(void* nativeEglImage, bool preserveContent) {
     EGLImageKHR image = s_egl.eglImportImageANDROID(m_display, (EGLImage)nativeEglImage);
 
     if (image == EGL_NO_IMAGE_KHR) return false;
 
-    // Assume nativeEglImage is compatible with ColorBufferGl's current dimensions and internal
-    // format.
+    // Assume nativeEglImage is compatible with ColorBuffer's current dimensions and internal format.
     EGLBoolean setInfoRes = s_egl.eglSetImageInfoANDROID(m_display, image, m_width, m_height, m_internalFormat);
 
     if (EGL_TRUE != setInfoRes) {
@@ -1135,7 +1197,7 @@
     return true;
 }
 
-std::vector<uint8_t> ColorBufferGl::getContents() {
+std::vector<uint8_t> ColorBuffer::getContents() {
     // Assume there is a current context.
     size_t bytes;
     readContents(&bytes, nullptr);
@@ -1144,20 +1206,20 @@
     return contents;
 }
 
-void ColorBufferGl::clearStorage() {
+void ColorBuffer::clearStorage() {
     s_gles2.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)NULL);
     s_egl.eglDestroyImageKHR(m_display, m_eglImage);
     m_eglImage = (EGLImageKHR)0;
 }
 
-void ColorBufferGl::restoreEglImage(EGLImageKHR image) {
+void ColorBuffer::restoreEglImage(EGLImageKHR image) {
     s_gles2.glBindTexture(GL_TEXTURE_2D, m_tex);
 
     m_eglImage = image;
     s_gles2.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)m_eglImage);
 }
 
-void ColorBufferGl::rebindEglImage(EGLImageKHR image, bool preserveContent) {
+void ColorBuffer::rebindEglImage(EGLImageKHR image, bool preserveContent) {
     RecursiveScopedContextBind context(m_helper);
 
     std::vector<uint8_t> contents;
@@ -1172,7 +1234,11 @@
     }
 }
 
-std::unique_ptr<BorrowedImageInfo> ColorBufferGl::getBorrowedImageInfo() {
+void ColorBuffer::setInUse(bool inUse) {
+    m_inUse = inUse;
+}
+
+std::unique_ptr<BorrowedImageInfo> ColorBuffer::getBorrowedImageInfo() {
     auto info = std::make_unique<BorrowedImageInfoGl>();
     info->id = mHndl;
     info->width = m_width;
@@ -1181,5 +1247,3 @@
     info->onCommandsIssued = [this]() { setSync(); };
     return info;
 }
-
-}  // namespace gfxstream
diff --git a/stream-servers/gl/ColorBufferGl.h b/stream-servers/gl/ColorBufferGl.h
index 5b8bd86..c9f62ce 100644
--- a/stream-servers/gl/ColorBufferGl.h
+++ b/stream-servers/gl/ColorBufferGl.h
@@ -33,6 +33,7 @@
 #include "aemu/base/ManagedDescriptor.hpp"
 #include "aemu/base/files/Stream.h"
 #include "render-utils/Renderer.h"
+#include "snapshot/LazySnapshotObj.h"
 
 // From ANGLE "src/common/angleutils.h"
 #define GL_BGR10_A2_ANGLEX 0x6AF9
@@ -45,54 +46,60 @@
 // related things:
 //
 //  - Every gralloc native buffer with HW read or write requirements will
-//    allocate a host ColorBufferGl instance. When gralloc_lock() is called,
-//    the guest will use ColorBufferGl::readPixels() to read the current content
+//    allocate a host ColorBuffer instance. When gralloc_lock() is called,
+//    the guest will use ColorBuffer::readPixels() to read the current content
 //    of the buffer. When gralloc_unlock() is later called, it will call
-//    ColorBufferGl::subUpdate() to send the updated pixels.
+//    ColorBuffer::subUpdate() to send the updated pixels.
 //
 //  - Every guest window EGLSurface is implemented by a host PBuffer
-//    (see WindowSurface.h) that can have a ColorBufferGl instance attached to
+//    (see WindowSurface.h) that can have a ColorBuffer instance attached to
 //    it (through WindowSurface::attachColorBuffer()). When such an attachment
 //    exists, WindowSurface::flushColorBuffer() will copy the PBuffer's
-//    pixel data into the ColorBufferGl. The latter can then be displayed
-//    in the client's UI sub-window with ColorBufferGl::post().
+//    pixel data into the ColorBuffer. The latter can then be displayed
+//    in the client's UI sub-window with ColorBuffer::post().
 //
 //  - Guest EGLImages are implemented as native gralloc buffers too.
 //    The guest glEGLImageTargetTexture2DOES() implementations will end up
-//    calling ColorBufferGl::bindToTexture() to bind the current context's
+//    calling ColorBuffer::bindToTexture() to bind the current context's
 //    GL_TEXTURE_2D to the buffer. Similarly, the guest versions of
 //    glEGLImageTargetRenderbufferStorageOES() will end up calling
-//    ColorBufferGl::bindToRenderbuffer().
+//    ColorBuffer::bindToRenderbuffer().
 //
 // This forces the implementation to use a host EGLImage to implement each
-// ColorBufferGl.
+// ColorBuffer.
 //
 // As an additional twist.
 
-namespace gfxstream {
-
-class ColorBufferGl {
+class ColorBuffer :
+        public android::snapshot::LazySnapshotObj<ColorBuffer> {
    public:
-    // Create a new ColorBufferGl instance.
-    // |display| is the host EGLDisplay handle.
-    // |width| and |height| are the buffer's dimensions in pixels.
-    // |internalFormat| is the internal OpenGL pixel format to use, valid
+    // Create a new ColorBuffer instance.
+    // |p_display| is the host EGLDisplay handle.
+    // |p_width| and |p_height| are the buffer's dimensions in pixels.
+    // |p_internalFormat| is the internal OpenGL pixel format to use, valid
     // values
     // are: GL_RGB, GL_RGB565, GL_RGBA, GL_RGB5_A1_OES and GL_RGBA4_OES.
     // Implementation is free to use something else though.
-    // |frameworkFormat| specifies the original format of the guest
-    // color buffer so that we know how to convert to |internalFormat|,
-    // if necessary (otherwise, frameworkFormat ==
+    // |p_frameworkFormat| specifies the original format of the guest
+    // color buffer so that we know how to convert to |p_internalFormat|,
+    // if necessary (otherwise, p_frameworkFormat ==
     // FRAMEWORK_FORMAT_GL_COMPATIBLE).
     // It is assumed underlying EGL has EGL_KHR_gl_texture_2D_image.
     // Returns NULL on failure.
-    // |fastBlitSupported|: whether or not this ColorBufferGl can be
+    // |fastBlitSupported|: whether or not this ColorBuffer can be
     // blitted and posted to swapchain without context switches.
-    static std::unique_ptr<ColorBufferGl> create(EGLDisplay display, int width, int height,
-                                                 GLint internalFormat,
-                                                 FrameworkFormat frameworkFormat, HandleType handle,
-                                                 ContextHelper* helper, TextureDraw* textureDraw,
-                                                 bool fastBlitSupported);
+    // |vulkanOnly|: whether or not the guest interacts entirely with Vulkan
+    // and does not use the GL based API.
+    static ColorBuffer* create(EGLDisplay p_display,
+                               int p_width,
+                               int p_height,
+                               GLint p_internalFormat,
+                               FrameworkFormat p_frameworkFormat,
+                               HandleType hndl,
+                               ContextHelper* helper,
+                               TextureDraw* textureDraw,
+                               bool fastBlitSupported,
+                               bool vulkanOnly = false);
 
     // Sometimes things happen and we need to reformat the GL texture
     // used. This function replaces the format of the underlying texture
@@ -100,14 +107,14 @@
     void reformat(GLint internalformat, GLenum type);
 
     // Destructor.
-    ~ColorBufferGl();
+    ~ColorBuffer();
 
-    // Return ColorBufferGl width and height in pixels
+    // Return ColorBuffer width and height in pixels
     GLuint getWidth() const { return m_width; }
     GLuint getHeight() const { return m_height; }
     GLint getInternalFormat() const { return m_internalFormat; }
 
-    // Read the ColorBufferGl instance's pixel values into host memory.
+    // Read the ColorBuffer instance's pixel values into host memory.
     void readPixels(int x,
                     int y,
                     int width,
@@ -119,7 +126,7 @@
     // to the size of width x height, then clipping a |rect| from the
     // screen defined by width x height.
     void readPixelsScaled(int width, int height, GLenum p_format, GLenum p_type, int skinRotation,
-                          emugl::Rect rect, void* pixels);
+                          void* pixels, emugl::Rect rect);
 
     // Read cached YUV pixel values into host memory.
     void readPixelsYUVCached(int x,
@@ -129,18 +136,23 @@
                              void* pixels,
                              uint32_t pixels_size);
 
-    void swapYUVTextures(FrameworkFormat texture_type, GLuint* textures);
+    void swapYUVTextures(uint32_t texture_type, uint32_t* textures);
 
-    // Update the ColorBufferGl instance's pixel values from host memory.
+    // Update the ColorBuffer instance's pixel values from host memory.
     // |p_format / p_type| are the desired OpenGL color buffer format
     // and data type.
     // Otherwise, subUpdate() will explicitly convert |pixels|
     // to be in |p_format|.
-    void subUpdate(int x, int y, int width, int height, GLenum p_format, GLenum p_type,
-                   const void* pixels);
+    void subUpdate(int x,
+                   int y,
+                   int width,
+                   int height,
+                   GLenum p_format,
+                   GLenum p_type,
+                   void* pixels);
     void subUpdateFromFrameworkFormat(int x, int y, int width, int height,
                                       FrameworkFormat fwkFormat, GLenum p_format, GLenum p_type,
-                                      const void* pixels);
+                                      void* pixels);
 
     // Completely replaces contents, assuming that |pixels| is a buffer
     // that is allocated and filled with the same format.
@@ -150,7 +162,7 @@
     // If the framework format is YUV, it will read back as raw YUV data.
     bool readContents(size_t* numBytes, void* pixels);
 
-    // Draw a ColorBufferGl instance, i.e. blit it to the current guest
+    // Draw a ColorBuffer instance, i.e. blit it to the current guest
     // framebuffer object / window surface. This doesn't display anything.
     bool draw();
 
@@ -162,38 +174,40 @@
     // |rotation| is the rotation angle in degrees, clockwise in the GL
     // coordinate space.
     bool post(GLuint tex, float rotation, float dx, float dy);
-    // Post this ColorBufferGl to the host native sub-window and apply
+    // Post this ColorBuffer to the host native sub-window and apply
     // the device screen overlay (if there is one).
     // |rotation| is the rotation angle in degrees, clockwise in the GL
     // coordinate space.
-    bool postViewportScaledWithOverlay(float rotation, float dx, float dy);
+    bool postWithOverlay(float rotation, float dx, float dy);
 
-    // Bind the current context's EGL_TEXTURE_2D texture to this ColorBufferGl's
+    // Bind the current context's EGL_TEXTURE_2D texture to this ColorBuffer's
     // EGLImage. This is intended to implement glEGLImageTargetTexture2DOES()
     // for all GLES versions.
     bool bindToTexture();
     bool bindToTexture2();
 
     // Bind the current context's EGL_RENDERBUFFER_OES render buffer to this
-    // ColorBufferGl's EGLImage. This is intended to implement
+    // ColorBuffer's EGLImage. This is intended to implement
     // glEGLImageTargetRenderbufferStorageOES() for all GLES versions.
     bool bindToRenderbuffer();
 
     // Copy the content of the current context's read surface to this
-    // ColorBufferGl. This is used from WindowSurface::flushColorBuffer().
+    // ColorBuffer. This is used from WindowSurface::flushColorBuffer().
     // Return true on success, false on failure (e.g. no current context).
     bool blitFromCurrentReadBuffer();
 
-    // Read the content of the whole ColorBufferGl as 32-bit RGBA pixels.
+    // Read the content of the whole ColorBuffer as 32-bit RGBA pixels.
     // |img| must be a buffer large enough (i.e. width * height * 4).
     void readback(unsigned char* img, bool readbackBgra = false);
     // readback() but async (to the specified |buffer|)
     void readbackAsync(GLuint buffer, bool readbackBgra = false);
 
     void onSave(android::base::Stream* stream);
-    static std::unique_ptr<ColorBufferGl> onLoad(android::base::Stream* stream,
-                                                 EGLDisplay p_display, ContextHelper* helper,
-                                                 TextureDraw* textureDraw, bool fastBlitSupported);
+    static ColorBuffer* onLoad(android::base::Stream* stream,
+                               EGLDisplay p_display,
+                               ContextHelper* helper,
+                               TextureDraw* textureDraw,
+                               bool fastBlitSupported);
 
     HandleType getHndl() const;
 
@@ -203,18 +217,21 @@
 
     std::unique_ptr<BorrowedImageInfo> getBorrowedImageInfo();
 
-    // ColorBufferGl backing change methods
+    // ColorBuffer backing change methods
     //
     // Change to opaque fd or opaque win32 handle-backed VkDeviceMemory
     // via GL_EXT_memory_objects
     bool importMemory(android::base::ManagedDescriptor externalDescriptor, uint64_t size,
-                      bool dedicated, bool linearTiling);
+                      bool dedicated, bool linearTiling, bool vulkanOnly);
     // Change to EGL native pixmap
     bool importEglNativePixmap(void* pixmap, bool preserveContent);
     // Change to some other native EGL image.  nativeEglImage must not have
     // been created from our s_egl.eglCreateImage.
     bool importEglImage(void* nativeEglImage, bool preserveContent);
 
+    void setInUse(bool inUse);
+    bool isInUse() const { return m_inUse; }
+
     void setSync(bool debug = false);
     void waitSync(bool debug = false);
     void setDisplay(uint32_t displayId) { m_displayId = displayId; }
@@ -225,24 +242,24 @@
     void restore();
 
 private:
- ColorBufferGl(EGLDisplay display, HandleType hndl, GLuint width, GLuint height,
-               ContextHelper* helper, TextureDraw* textureDraw);
- // Helper function to get contents.
- std::vector<uint8_t> getContents();
- // Helper function to clear current EGL image.
- void clearStorage();
- // Helper function to bind EGL image as texture. Assumes storage cleared.
- void restoreEglImage(EGLImageKHR image);
- // Helper function that does the above two operations in one go.
- void rebindEglImage(EGLImageKHR image, bool preserveContent);
+    ColorBuffer(EGLDisplay display, HandleType hndl, ContextHelper* helper,
+                TextureDraw* textureDraw);
+    // Helper function to get contents.
+    std::vector<uint8_t> getContents();
+    // Helper function to clear current EGL image.
+    void clearStorage();
+    // Helper function to bind EGL image as texture. Assumes storage cleared.
+    void restoreEglImage(EGLImageKHR image);
+    // Helper function that does the above two operations in one go.
+    void rebindEglImage(EGLImageKHR image, bool preserveContent);
 
 private:
     GLuint m_tex = 0;
     GLuint m_blitTex = 0;
     EGLImageKHR m_eglImage = nullptr;
     EGLImageKHR m_blitEGLImage = nullptr;
-    const GLuint m_width = 0;
-    const GLuint m_height = 0;
+    GLuint m_width = 0;
+    GLuint m_height = 0;
     GLuint m_fbo = 0;
     GLint m_internalFormat = 0;
     GLint m_sizedInternalFormat = 0;
@@ -289,6 +306,21 @@
     bool m_BRSwizzle = false;
 };
 
-typedef std::shared_ptr<ColorBufferGl> ColorBufferGlPtr;
+typedef std::shared_ptr<ColorBuffer> ColorBufferPtr;
 
-}  // namespace gfxstream
+struct ColorBufferRef {
+    ColorBufferPtr cb;
+    uint32_t refcount;  // number of client-side references
+
+    // Tracks whether opened at least once. In O+,
+    // color buffers can be created/closed immediately,
+    // but then registered (opened) afterwards.
+    bool opened;
+
+    // Tracks the time when this buffer got a close request while not being
+    // opened yet.
+    uint64_t closedTs;
+};
+
+typedef std::unordered_map<HandleType, ColorBufferRef> ColorBufferMap;
+typedef std::unordered_multiset<HandleType> ColorBufferSet;
diff --git a/stream-servers/gl/DisplayGl.cpp b/stream-servers/gl/DisplayGl.cpp
index 915394f..86c9f49 100644
--- a/stream-servers/gl/DisplayGl.cpp
+++ b/stream-servers/gl/DisplayGl.cpp
@@ -53,14 +53,16 @@
                 mTextureDraw->prepareForDrawLayer();
                 hasDrawLayer = true;
             }
-            layer.colorBuffer->glOpPostLayer(*layer.layerOptions, post.frameWidth,
-                                             post.frameHeight);
+            layer.colorBuffer->postLayer(*layer.layerOptions,
+                                         post.frameWidth,
+                                         post.frameHeight);
         } else if (layer.overlayOptions) {
             if (hasDrawLayer) {
                 ERR("Cannot mix colorBuffer.postLayer with postWithOverlay!");
             }
-            layer.colorBuffer->glOpPostViewportScaledWithOverlay(
-                layer.overlayOptions->rotation, layer.overlayOptions->dx, layer.overlayOptions->dy);
+            layer.colorBuffer->postWithOverlay(layer.overlayOptions->rotation,
+                                               layer.overlayOptions->dx,
+                                               layer.overlayOptions->dy);
         }
     }
     if (hasDrawLayer) {
diff --git a/stream-servers/gl/DisplayGl.h b/stream-servers/gl/DisplayGl.h
index a82f520..c421481 100644
--- a/stream-servers/gl/DisplayGl.h
+++ b/stream-servers/gl/DisplayGl.h
@@ -14,17 +14,17 @@
 
 #pragma once
 
+#include <atomic>
+#include <future>
+
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
 
-#include <atomic>
-#include <future>
-
-#include "ColorBuffer.h"
 #include "Display.h"
 #include "Hwc2.h"
+#include "gl/ColorBufferGl.h"
 
 class DisplayGl : public gfxstream::Display {
   public:
diff --git a/stream-servers/gl/EmulatedEglWindowSurface.cpp b/stream-servers/gl/EmulatedEglWindowSurface.cpp
index 426b3a4..9899aaa 100644
--- a/stream-servers/gl/EmulatedEglWindowSurface.cpp
+++ b/stream-servers/gl/EmulatedEglWindowSurface.cpp
@@ -122,7 +122,7 @@
         }
     }
 
-    mAttachedColorBuffer->glOpBlitFromCurrentReadBuffer();
+    mAttachedColorBuffer->blitFromCurrentReadBuffer();
 
     if (needToSet) {
         // restore current context/surface
diff --git a/stream-servers/gl/EmulatedEglWindowSurface.h b/stream-servers/gl/EmulatedEglWindowSurface.h
index a3a0108..779a4c1 100644
--- a/stream-servers/gl/EmulatedEglWindowSurface.h
+++ b/stream-servers/gl/EmulatedEglWindowSurface.h
@@ -16,14 +16,13 @@
 
 #pragma once
 
+#include <memory>
+#include <unordered_set>
+#include <unordered_map>
+
 #include <EGL/egl.h>
 #include <GLES/gl.h>
 
-#include <memory>
-#include <unordered_map>
-#include <unordered_set>
-
-#include "ColorBuffer.h"
 #include "Handle.h"
 #include "gl/ColorBufferGl.h"
 #include "gl/EmulatedEglContext.h"
diff --git a/stream-servers/gl/EmulationGl.cpp b/stream-servers/gl/EmulationGl.cpp
index aa72329..86fcbe4 100644
--- a/stream-servers/gl/EmulationGl.cpp
+++ b/stream-servers/gl/EmulationGl.cpp
@@ -594,29 +594,6 @@
     }
 }
 
-ContextHelper* EmulationGl::getColorBufferContextHelper() {
-    if (!mPbufferSurface) {
-        return nullptr;
-    }
-
-    const auto* surfaceGl = static_cast<const DisplaySurfaceGl*>(mPbufferSurface->getImpl());
-    return surfaceGl->getContextHelper();
-}
-
-std::unique_ptr<ColorBufferGl> EmulationGl::createColorBuffer(uint32_t width, uint32_t height,
-                                                              GLenum internalFormat,
-                                                              FrameworkFormat frameworkFormat,
-                                                              HandleType handle) {
-    return ColorBufferGl::create(mEglDisplay, width, height, internalFormat, frameworkFormat,
-                                 handle, getColorBufferContextHelper(), mTextureDraw.get(),
-                                 isFastBlitSupported());
-}
-
-std::unique_ptr<ColorBufferGl> EmulationGl::loadColorBuffer(android::base::Stream* stream) {
-    return ColorBufferGl::onLoad(stream, mEglDisplay, getColorBufferContextHelper(),
-                                 mTextureDraw.get(), isFastBlitSupported());
-}
-
 std::unique_ptr<EmulatedEglContext> EmulationGl::createEmulatedEglContext(
         uint32_t emulatedEglConfigIndex,
         const EmulatedEglContext* sharedContext,
diff --git a/stream-servers/gl/EmulationGl.h b/stream-servers/gl/EmulationGl.h
index 6146561..65168ca 100644
--- a/stream-servers/gl/EmulationGl.h
+++ b/stream-servers/gl/EmulationGl.h
@@ -14,24 +14,24 @@
 
 #pragma once
 
+#include <array>
+#include <memory>
+#include <string>
+#include <optional>
+#include <unordered_set>
+
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 #include <GLES/gl.h>
 #include <GLES3/gl3.h>
 
-#include <array>
-#include <memory>
-#include <optional>
-#include <string>
-#include <unordered_set>
-
-#include "ColorBufferGl.h"
+#include "ContextHelper.h"
 #include "Compositor.h"
 #include "CompositorGl.h"
-#include "ContextHelper.h"
 #include "Display.h"
 #include "DisplayGl.h"
 #include "DisplaySurface.h"
+#include "EmulatedEglContext.h"
 #include "EmulatedEglConfig.h"
 #include "EmulatedEglContext.h"
 #include "EmulatedEglFenceSync.h"
@@ -40,7 +40,6 @@
 #include "OpenGLESDispatch/GLESv2Dispatch.h"
 #include "ReadbackWorkerGl.h"
 #include "TextureDraw.h"
-#include "aemu/base/files/Stream.h"
 
 #define EGL_NO_CONFIG ((EGLConfig)0)
 
@@ -86,18 +85,14 @@
 
     ReadbackWorkerGl* getReadbackWorker() { return mReadbackWorkerGl.get(); }
 
+    // TODO(b/233939967): Remove after adding ColorBufferGl and EmulationGl::createColorBuffer().
+    TextureDraw* getTextureDraw() const { return mTextureDraw.get(); }
+
     using GlesUuid = std::array<uint8_t, GL_UUID_SIZE_EXT>;
     const std::optional<GlesUuid> getGlesDeviceUuid() const { return mGlesDeviceUuid; }
 
     void setUseBoundSurfaceContextForDisplay(bool use);
 
-    std::unique_ptr<ColorBufferGl> createColorBuffer(uint32_t width, uint32_t height,
-                                                     GLenum internalFormat,
-                                                     FrameworkFormat frameworkFormat,
-                                                     HandleType handle);
-
-    std::unique_ptr<ColorBufferGl> loadColorBuffer(android::base::Stream* stream);
-
     std::unique_ptr<EmulatedEglContext> createEmulatedEglContext(
         uint32_t emulatedEglConfigIndex,
         const EmulatedEglContext* shareContext,
@@ -133,8 +128,6 @@
 
     EmulationGl() = default;
 
-    ContextHelper* getColorBufferContextHelper();
-
     EGLDisplay mEglDisplay = EGL_NO_DISPLAY;
     EGLint mEglVersionMajor = 0;
     EGLint mEglVersionMinor = 0;
diff --git a/stream-servers/gl/ReadbackWorkerGl.cpp b/stream-servers/gl/ReadbackWorkerGl.cpp
index 426f86b..41a0c93 100644
--- a/stream-servers/gl/ReadbackWorkerGl.cpp
+++ b/stream-servers/gl/ReadbackWorkerGl.cpp
@@ -17,7 +17,6 @@
 
 #include <string.h>
 
-#include "ColorBuffer.h"
 #include "ContextHelper.h"
 #include "OpenGLESDispatch/DispatchTables.h"
 #include "OpenGLESDispatch/EGLDispatch.h"
@@ -166,7 +165,7 @@
         r.m_readbackCount++;
         r.mPrevReadPixelsIndex = readAt;
 
-        cb->glOpReadbackAsync(r.mBuffers[readAt], readbackBgra);
+        cb->readbackAsync(r.mBuffers[readAt], readbackBgra);
 
         // It's possible to post callback before any of the async readbacks
         // have written any data yet, which results in a black frame.  Safer
diff --git a/stream-servers/gl/YUVConverter.cpp b/stream-servers/gl/YUVConverter.cpp
index d06f768..f9735a0 100644
--- a/stream-servers/gl/YUVConverter.cpp
+++ b/stream-servers/gl/YUVConverter.cpp
@@ -461,8 +461,7 @@
     s_gles2.glActiveTexture(GL_TEXTURE0);
 }
 
-static void readYUVTex(GLuint tex, FrameworkFormat format, YUVPlane plane, void* pixels,
-                       uint32_t pixelsStride) {
+static void readYUVTex(GLuint tex, FrameworkFormat format, YUVPlane plane, void* pixels) {
     YUV_DEBUG_LOG("format%d plane:%d pixels:%p", format, plane, pixels);
 
     GLuint prevTexture = 0;
@@ -471,10 +470,6 @@
     GLint prevAlignment = 0;
     s_gles2.glGetIntegerv(GL_PACK_ALIGNMENT, &prevAlignment);
     s_gles2.glPixelStorei(GL_PACK_ALIGNMENT, 1);
-    GLint prevStride = 0;
-    s_gles2.glGetIntegerv(GL_PACK_ROW_LENGTH, &prevStride);
-    s_gles2.glPixelStorei(GL_PACK_ROW_LENGTH, pixelsStride);
-
     const GLenum pixelFormat = getGlPixelFormat(format, plane);
     const GLenum pixelType = getGlPixelType(format, plane);
     if (s_gles2.glGetTexImage) {
@@ -483,7 +478,6 @@
         YUV_DEBUG_LOG("empty glGetTexImage");
     }
 
-    s_gles2.glPixelStorei(GL_PACK_ROW_LENGTH, prevStride);
     s_gles2.glPixelStorei(GL_PACK_ALIGNMENT, prevAlignment);
     s_gles2.glBindTexture(GL_TEXTURE_2D, prevTexture);
 }
@@ -858,11 +852,10 @@
                   &vWidth, &vHeight, &vOffsetBytes, &vStridePixels, &vStrideBytes);
 
     if (isInterleaved(mFormat)) {
-        readYUVTex(mTextureV, mFormat, YUVPlane::UV, pixels + std::min(uOffsetBytes, vOffsetBytes),
-                   uStridePixels);
+        readYUVTex(mTextureV, mFormat, YUVPlane::UV, pixels + std::min(uOffsetBytes, vOffsetBytes));
     } else {
-        readYUVTex(mTextureU, mFormat, YUVPlane::U, pixels + uOffsetBytes, uStridePixels);
-        readYUVTex(mTextureV, mFormat, YUVPlane::V, pixels + vOffsetBytes, vStridePixels);
+        readYUVTex(mTextureU, mFormat, YUVPlane::U, pixels + uOffsetBytes);
+        readYUVTex(mTextureV, mFormat, YUVPlane::V, pixels + vOffsetBytes);
     }
 
     if (mFormat == FRAMEWORK_FORMAT_NV12 && mColorBufferFormat == FRAMEWORK_FORMAT_YUV_420_888) {
@@ -870,10 +863,12 @@
     }
 
     // Read the Y plane last because so that we can use it as a scratch space.
-    readYUVTex(mTextureY, mFormat, YUVPlane::Y, pixels + yOffsetBytes, yStridePixels);
+    readYUVTex(mTextureY, mFormat, YUVPlane::Y, pixels + yOffsetBytes);
 }
 
-void YUVConverter::swapTextures(FrameworkFormat format, GLuint* textures) {
+void YUVConverter::swapTextures(uint32_t type, uint32_t* textures) {
+    FrameworkFormat format = static_cast<FrameworkFormat>(type);
+
     if (isInterleaved(format)) {
         std::swap(textures[0], mTextureY);
         std::swap(textures[1], mTextureU);
diff --git a/stream-servers/gl/YUVConverter.h b/stream-servers/gl/YUVConverter.h
index 23dde61..8cddee1 100644
--- a/stream-servers/gl/YUVConverter.h
+++ b/stream-servers/gl/YUVConverter.h
@@ -71,7 +71,7 @@
     // if size mismatches, will read nothing.
     void readPixels(uint8_t* pixels, uint32_t pixels_size);
 
-    void swapTextures(FrameworkFormat type, GLuint* textures);
+    void swapTextures(uint32_t type, uint32_t* textures);
 
     // public so other classes can call
     static void createYUVGLTex(GLenum textureUnit,
diff --git a/stream-servers/tests/FrameBuffer_unittest.cpp b/stream-servers/tests/FrameBuffer_unittest.cpp
index 410986d..63915af 100644
--- a/stream-servers/tests/FrameBuffer_unittest.cpp
+++ b/stream-servers/tests/FrameBuffer_unittest.cpp
@@ -540,14 +540,14 @@
     EXPECT_TRUE(mFb->isFastBlitSupported());
 
     mFb->lock();
-    EXPECT_EQ(mFb->isFastBlitSupported(), mFb->findColorBuffer(handle)->glOpIsFastBlitSupported());
+    EXPECT_EQ(mFb->isFastBlitSupported(), mFb->findColorBuffer(handle)->isFastBlitSupported());
     mFb->unlock();
 
     saveSnapshot();
     loadSnapshot();
 
     mFb->lock();
-    EXPECT_EQ(mFb->isFastBlitSupported(), mFb->findColorBuffer(handle)->glOpIsFastBlitSupported());
+    EXPECT_EQ(mFb->isFastBlitSupported(), mFb->findColorBuffer(handle)->isFastBlitSupported());
     mFb->unlock();
 
     mFb->closeColorBuffer(handle);
diff --git a/stream-servers/vulkan/VkAndroidNativeBuffer.cpp b/stream-servers/vulkan/VkAndroidNativeBuffer.cpp
index 07a1666..096cc8c 100644
--- a/stream-servers/vulkan/VkAndroidNativeBuffer.cpp
+++ b/stream-servers/vulkan/VkAndroidNativeBuffer.cpp
@@ -511,6 +511,8 @@
         // If we used the Vulkan image without copying it back
         // to the CPU, reset the layout to PRESENT.
         if (anbInfo->useVulkanNativeImage) {
+            fb->setColorBufferInUse(anbInfo->colorBufferHandle, true);
+
             VkCommandBufferBeginInfo beginInfo = {
                 VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
                 0,
@@ -759,6 +761,7 @@
 
     if (anbInfo->useVulkanNativeImage) {
         VK_ANB_DEBUG_OBJ(anbInfoPtr, "using native image, so use sync thread to wait");
+        fb->setColorBufferInUse(anbInfo->colorBufferHandle, false);
         // Queue wait to sync thread with completion callback
         // Pass anbInfo by value to get a ref
         SyncThread::get()->triggerGeneral(