Add RenderThreadInfoGl

... and move the GL decoders into a RenderThreadInfoGl which, for
now, is always initialized. This is useful for upcoming changes to
eventually turn off the GL bits when running in Vulkan only mode.

Bug: b/233939967
Test: cvd start --gpu_mode=gfxstream
Test: cvd start --gpu_mode=gfxstream --enable_gpu_angle=true
Change-Id: Id7dd0b078d4870e51dff5a5e7ad2e012f55a1c8b
diff --git a/stream-servers/Android.bp b/stream-servers/Android.bp
index c1969e6..96416d1 100644
--- a/stream-servers/Android.bp
+++ b/stream-servers/Android.bp
@@ -68,14 +68,15 @@
         "ReadBuffer.cpp",
         "render_api.cpp",
         "RenderChannelImpl.cpp",
+        "RenderThread.cpp",
         "RenderThreadInfo.cpp",
+        "RenderThreadInfoGl.cpp",
         "RingStream.cpp",
         "SyncThread.cpp",
         "TextureDraw.cpp",
         "TextureResize.cpp",
         "WindowSurface.cpp",
         "YUVConverter.cpp",
-        "RenderThread.cpp",
         "RenderContext.cpp",
         "RenderControl.cpp",
         "RenderWindow.cpp",
diff --git a/stream-servers/CMakeLists.txt b/stream-servers/CMakeLists.txt
index f72cacc..ad5e7e7 100644
--- a/stream-servers/CMakeLists.txt
+++ b/stream-servers/CMakeLists.txt
@@ -38,6 +38,7 @@
     render_api.cpp
     RenderChannelImpl.cpp
     RenderThreadInfo.cpp
+    RenderThreadInfoGl.cpp
     RingStream.cpp
     SyncThread.cpp
     TextureDraw.cpp
diff --git a/stream-servers/ColorBuffer.cpp b/stream-servers/ColorBuffer.cpp
index f5ad397..f301f7f 100644
--- a/stream-servers/ColorBuffer.cpp
+++ b/stream-servers/ColorBuffer.cpp
@@ -23,7 +23,7 @@
 #include "Debug.h"
 #include "OpenGLESDispatch/DispatchTables.h"
 #include "OpenGLESDispatch/EGLDispatch.h"
-#include "RenderThreadInfo.h"
+#include "RenderThreadInfoGl.h"
 #include "TextureDraw.h"
 #include "TextureResize.h"
 #include "YUVConverter.h"
@@ -645,7 +645,12 @@
 }
 
 bool ColorBuffer::blitFromCurrentReadBuffer() {
-    RenderThreadInfo* tInfo = RenderThreadInfo::get();
+    RenderThreadInfoGl* const tInfo = RenderThreadInfoGl::get();
+    if (!tInfo) {
+        GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
+            << "Render thread GL not available.";
+    }
+
     if (!tInfo->currContext.get()) {
         // no Current context
         return false;
@@ -808,7 +813,12 @@
         return false;
     }
 
-    RenderThreadInfo* tInfo = RenderThreadInfo::get();
+    RenderThreadInfoGl* const tInfo = RenderThreadInfoGl::get();
+    if (!tInfo) {
+        GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
+            << "Render thread GL not available.";
+    }
+
     if (!tInfo->currContext.get()) {
         return false;
     }
@@ -835,7 +845,13 @@
     if (!m_eglImage) {
         return false;
     }
-    RenderThreadInfo* tInfo = RenderThreadInfo::get();
+
+    RenderThreadInfoGl* const tInfo = RenderThreadInfoGl::get();
+    if (!tInfo) {
+        GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
+            << "Render thread GL not available.";
+    }
+
     if (!tInfo->currContext.get()) {
         return false;
     }
diff --git a/stream-servers/FrameBuffer.cpp b/stream-servers/FrameBuffer.cpp
index 9b76f80..95cdd8e 100644
--- a/stream-servers/FrameBuffer.cpp
+++ b/stream-servers/FrameBuffer.cpp
@@ -31,6 +31,8 @@
 #include "OpenGLESDispatch/EGLDispatch.h"
 #include "RenderControl.h"
 #include "RenderThreadInfo.h"
+#include "RenderThreadInfoGl.h"
+#include "SyncThread.h"
 #include "YUVConverter.h"
 #include "base/LayoutResolver.h"
 #include "base/Lock.h"
@@ -1731,7 +1733,11 @@
         if (puid) {
             m_procOwnedRenderContext[puid].insert(ret);
         } else { // legacy path to manage context lifetime by threads
-            tinfo->m_contextSet.insert(ret);
+            if (!tinfo->m_glInfo) {
+                GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
+                    << "Render thread GL not available.";
+            }
+            tinfo->m_glInfo->m_contextSet.insert(ret);
         }
     } else {
         ret = 0;
@@ -1764,19 +1770,46 @@
         if (puid) {
             m_procOwnedWindowSurfaces[puid].insert(ret);
         } else { // legacy path to manage window surface lifetime by threads
-            tInfo->m_windowSet.insert(ret);
+            if (!tInfo->m_glInfo) {
+                GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
+                    << "Render thread GL not available.";
+            }
+            tInfo->m_glInfo->m_windowSet.insert(ret);
         }
     }
 
     return ret;
 }
 
-void FrameBuffer::drainRenderContext() {
-    if (m_shuttingDown) {
+void FrameBuffer::drainRenderThreadResources() {
+    // If we're already exiting then snapshot should not contain
+    // this thread information at all.
+    if (isShuttingDown()) {
         return;
     }
 
-    RenderThreadInfo* const tinfo = RenderThreadInfo::get();
+    // Release references to the current thread's context/surfaces if any
+    bindContext(0, 0, 0);
+
+    drainRenderThreadWindowSurfaces();
+    drainRenderThreadContexts();
+
+    if (!s_egl.eglReleaseThread()) {
+        ERR("Error: RenderThread @%p failed to eglReleaseThread()", this);
+    }
+}
+
+void FrameBuffer::drainRenderThreadContexts() {
+    if (isShuttingDown()) {
+        return;
+    }
+
+    RenderThreadInfoGl* const tinfo = RenderThreadInfoGl::get();
+    if (!tinfo) {
+        GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
+            << "Render thread GL not available.";
+    }
+
     if (tinfo->m_contextSet.empty()) {
         return;
     }
@@ -1789,11 +1822,17 @@
     tinfo->m_contextSet.clear();
 }
 
-void FrameBuffer::drainWindowSurface() {
-    if (m_shuttingDown) {
+void FrameBuffer::drainRenderThreadWindowSurfaces() {
+    if (isShuttingDown()) {
         return;
     }
-    RenderThreadInfo* const tinfo = RenderThreadInfo::get();
+
+    RenderThreadInfoGl* const tinfo = RenderThreadInfoGl::get();
+    if (!tinfo) {
+        GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
+            << "Render thread GL not available.";
+    }
+
     if (tinfo->m_windowSet.empty()) {
         return;
     }
@@ -1847,7 +1886,11 @@
             ite->second.erase(p_context);
         }
     } else {
-        tinfo->m_contextSet.erase(p_context);
+        if (!tinfo->m_glInfo) {
+            GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
+                << "Render thread GL not available.";
+        }
+        tinfo->m_glInfo->m_contextSet.erase(p_context);
     }
 }
 
@@ -1890,7 +1933,11 @@
                 ite->second.erase(p_surface);
             }
         } else {
-            tinfo->m_windowSet.erase(p_surface);
+            if (!tinfo->m_glInfo) {
+                GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
+                    << "Render thread GL not available.";
+            }
+            tinfo->m_glInfo->m_windowSet.erase(p_surface);
         }
     }
     return colorBuffersToCleanUp;
@@ -2626,7 +2673,12 @@
     //
     // Bind the surface(s) to the context
     //
-    RenderThreadInfo* tinfo = RenderThreadInfo::get();
+    RenderThreadInfoGl* const tinfo = RenderThreadInfoGl::get();
+    if (!tinfo) {
+        GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
+            << "Render thread GL not available.";
+    }
+
     WindowSurfacePtr bindDraw, bindRead;
     if (draw.get() == NULL && read.get() == NULL) {
         // Unbind the current read and draw surfaces from the context
diff --git a/stream-servers/FrameBuffer.h b/stream-servers/FrameBuffer.h
index a06023d..ffeb1a9 100644
--- a/stream-servers/FrameBuffer.h
+++ b/stream-servers/FrameBuffer.h
@@ -252,14 +252,19 @@
     void createBufferWithHandle(uint64_t size, HandleType handle);
 
     // Call this function when a render thread terminates to destroy all
-    // the remaining contexts it created. Necessary to avoid leaking host
-    // contexts when a guest application crashes, for example.
-    void drainRenderContext();
+    // resources it created. Necessary to avoid leaking host resources
+    // when a guest application crashes, for example.
+    void drainRenderThreadResources();
 
     // Call this function when a render thread terminates to destroy all
-    // remaining window surfqce it created. Necessary to avoid leaking
+    // the remaining contexts it created. Necessary to avoid leaking host
+    // contexts when a guest application crashes, for example.
+    void drainRenderThreadContexts();
+
+    // Call this function when a render thread terminates to destroy all
+    // remaining window surface it created. Necessary to avoid leaking
     // host buffers when a guest application crashes, for example.
-    void drainWindowSurface();
+    void drainRenderThreadWindowSurfaces();
 
     // Destroy a given RenderContext instance. |p_context| is its handle
     // value as returned by createRenderContext().
diff --git a/stream-servers/RenderControl.cpp b/stream-servers/RenderControl.cpp
index f950ec3..44786f8 100644
--- a/stream-servers/RenderControl.cpp
+++ b/stream-servers/RenderControl.cpp
@@ -31,6 +31,7 @@
 #include "OpenGLESDispatch/EGLDispatch.h"
 #include "RenderContext.h"
 #include "RenderThreadInfo.h"
+#include "RenderThreadInfoGl.h"
 #include "SyncThread.h"
 #include "base/Tracing.h"
 #include "host-common/dma_device.h"
@@ -426,7 +427,7 @@
 }
 
 static EGLint rcGetGLString(EGLenum name, void* buffer, EGLint bufferSize) {
-    RenderThreadInfo *tInfo = RenderThreadInfo::get();
+    RenderThreadInfoGl* const tInfo = RenderThreadInfoGl::get();
 
     // whatever we end up returning,
     // it will have a terminating \0,
@@ -1163,7 +1164,11 @@
     // rcTriggerWait is registered.
     emugl_sync_register_trigger_wait(rcTriggerWait);
 
-    RenderThreadInfo *tInfo = RenderThreadInfo::get();
+    RenderThreadInfoGl* const tInfo = RenderThreadInfoGl::get();
+    if (!tInfo) {
+        GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
+            << "Render thread GL not available.";
+    }
 
     if (!tInfo->currContext) {
         auto fb = FrameBuffer::getFB();
@@ -1200,7 +1205,12 @@
 static EGLint rcClientWaitSyncKHR(uint64_t handle,
                                   EGLint flags,
                                   uint64_t timeout) {
-    RenderThreadInfo *tInfo = RenderThreadInfo::get();
+    RenderThreadInfoGl* const tInfo = RenderThreadInfoGl::get();
+    if (!tInfo) {
+        GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
+            << "Render thread GL not available.";
+    }
+
     FrameBuffer *fb = FrameBuffer::getFB();
 
     EGLSYNC_DPRINT("handle=0x%lx flags=0x%x timeout=%" PRIu64,
@@ -1232,7 +1242,12 @@
 
 static void rcWaitSyncKHR(uint64_t handle,
                                   EGLint flags) {
-    RenderThreadInfo *tInfo = RenderThreadInfo::get();
+    RenderThreadInfoGl* const tInfo = RenderThreadInfoGl::get();
+    if (!tInfo) {
+        GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
+            << "Render thread GL not available.";
+    }
+
     FrameBuffer *fb = FrameBuffer::getFB();
 
     EGLSYNC_DPRINT("handle=0x%lx flags=0x%x", handle, flags);
diff --git a/stream-servers/RenderThread.cpp b/stream-servers/RenderThread.cpp
index 88eb3b8..f3eb05a 100644
--- a/stream-servers/RenderThread.cpp
+++ b/stream-servers/RenderThread.cpp
@@ -268,8 +268,8 @@
     //
     // initialize decoders
     //
-    tInfo.m_glDec.initGL(gles1_dispatch_get_proc_func, nullptr);
-    tInfo.m_gl2Dec.initGL(gles2_dispatch_get_proc_func, nullptr);
+    tInfo.initGl();
+
     initRenderControlContext(&tInfo.m_rcDec);
 
     if (!mChannel && !mRingStream) {
@@ -380,16 +380,7 @@
                 // restores the contexts from the handles, so check again here.
 
                 tInfo.postLoadRefreshCurrentContextSurfacePtrs();
-                // We just loaded from a snapshot, need to initialize / bind
-                // the contexts.
                 needRestoreFromSnapshot = false;
-                HandleType ctx = tInfo.currContext ? tInfo.currContext->getHndl()
-                        : 0;
-                HandleType draw = tInfo.currDrawSurf ? tInfo.currDrawSurf->getHndl()
-                        : 0;
-                HandleType read = tInfo.currReadSurf ? tInfo.currReadSurf->getHndl()
-                        : 0;
-                FrameBuffer::getFB()->bindContext(ctx, draw, read);
             }
         }
 
@@ -482,26 +473,28 @@
                 FrameBuffer::getFB()->lockContextStructureRead();
             }
 
-            {
-                last = tInfo.m_glDec.decode(
-                        readBuf.buf(), readBuf.validData(), ioStream, &checksumCalc);
-                if (last > 0) {
-                    progress = true;
-                    readBuf.consume(last);
+            if (tInfo.m_glInfo) {
+                {
+                    last = tInfo.m_glInfo->m_glDec.decode(
+                            readBuf.buf(), readBuf.validData(), ioStream, &checksumCalc);
+                    if (last > 0) {
+                        progress = true;
+                        readBuf.consume(last);
+                    }
                 }
-            }
 
-            //
-            // try to process some of the command buffer using the GLESv2
-            // decoder
-            //
-            {
-                last = tInfo.m_gl2Dec.decode(readBuf.buf(), readBuf.validData(),
-                                             ioStream, &checksumCalc);
+                //
+                // try to process some of the command buffer using the GLESv2
+                // decoder
+                //
+                {
+                    last = tInfo.m_glInfo->m_gl2Dec.decode(readBuf.buf(), readBuf.validData(),
+                                                           ioStream, &checksumCalc);
 
-                if (last > 0) {
-                    progress = true;
-                    readBuf.consume(last);
+                    if (last > 0) {
+                        progress = true;
+                        readBuf.consume(last);
+                    }
                 }
             }
 
@@ -530,22 +523,7 @@
         fclose(dumpFP);
     }
 
-    // Don't check for snapshots here: if we're already exiting then snapshot
-    // should not contain this thread information at all.
-    if (!FrameBuffer::getFB()->isShuttingDown()) {
-        // Release references to the current thread's context/surfaces if any
-        FrameBuffer::getFB()->bindContext(0, 0, 0);
-        if (tInfo.currContext || tInfo.currDrawSurf || tInfo.currReadSurf) {
-            ERR("ERROR: RenderThread exiting with current context/surfaces");
-        }
-
-        FrameBuffer::getFB()->drainWindowSurface();
-        FrameBuffer::getFB()->drainRenderContext();
-    }
-
-    if (!s_egl.eglReleaseThread()) {
-        ERR("Error: RenderThread @%p failed to eglReleaseThread()", this);
-    }
+    FrameBuffer::getFB()->drainRenderThreadResources();
 
     setFinished();
 
diff --git a/stream-servers/RenderThreadInfo.cpp b/stream-servers/RenderThreadInfo.cpp
index 2e753bf..4bf8dad 100644
--- a/stream-servers/RenderThreadInfo.cpp
+++ b/stream-servers/RenderThreadInfo.cpp
@@ -16,12 +16,8 @@
 
 #include "RenderThreadInfo.h"
 
-#include "base/Lookup.h"
-#include "base/StreamSerializing.h"
 #include "base/Lock.h"
 
-#include "FrameBuffer.h"
-
 #include <unordered_map>
 #include <unordered_set>
 
@@ -62,76 +58,18 @@
     }
 }
 
+void RenderThreadInfo::initGl() {
+    m_glInfo.emplace();
+}
+
 void RenderThreadInfo::onSave(Stream* stream) {
-    if (currContext) {
-        stream->putBe32(currContext->getHndl());
-    } else {
-        stream->putBe32(0);
-    }
-    if (currDrawSurf) {
-        stream->putBe32(currDrawSurf->getHndl());
-    } else {
-        stream->putBe32(0);
-    }
-    if (currReadSurf) {
-        stream->putBe32(currReadSurf->getHndl());
-    } else {
-        stream->putBe32(0);
-    }
-
-    saveCollection(stream, m_contextSet, [](Stream* stream, HandleType val) {
-        stream->putBe32(val);
-    });
-    saveCollection(stream, m_windowSet, [](Stream* stream, HandleType val) {
-        stream->putBe32(val);
-    });
-
-    stream->putBe64(m_puid);
-
-    // No need to associate render threads with sync threads
-    // if there is a global sync thread. This is only needed
-    // to maintain backward compatibility with snapshot file format.
-    // (Used to be: stream->putBe64(syncThreadAlias))
-    stream->putBe64(0);
+    m_glInfo->onSave(stream);
 }
 
 bool RenderThreadInfo::onLoad(Stream* stream) {
-    FrameBuffer* fb = FrameBuffer::getFB();
-    assert(fb);
-    HandleType ctxHndl = stream->getBe32();
-    HandleType drawSurf = stream->getBe32();
-    HandleType readSurf = stream->getBe32();
-    currContextHandleFromLoad = ctxHndl;
-    currDrawSurfHandleFromLoad = drawSurf;
-    currReadSurfHandleFromLoad = readSurf;
-
-    fb->lock();
-    currContext = fb->getContext_locked(ctxHndl);
-    currDrawSurf = fb->getWindowSurface_locked(drawSurf);
-    currReadSurf = fb->getWindowSurface_locked(readSurf);
-    fb->unlock();
-
-    loadCollection(stream, &m_contextSet, [](Stream* stream) {
-        return stream->getBe32();
-    });
-    loadCollection(stream, &m_windowSet, [](Stream* stream) {
-        return stream->getBe32();
-    });
-
-    m_puid = stream->getBe64();
-
-    // (Used to be: syncThreadAlias = stream->getBe64())
-    stream->getBe64();
-
-    return true;
+    return m_glInfo->onLoad(stream);
 }
 
 void RenderThreadInfo::postLoadRefreshCurrentContextSurfacePtrs() {
-    FrameBuffer* fb = FrameBuffer::getFB();
-    assert(fb);
-    fb->lock();
-    currContext = fb->getContext_locked(currContextHandleFromLoad);
-    currDrawSurf = fb->getWindowSurface_locked(currDrawSurfHandleFromLoad);
-    currReadSurf = fb->getWindowSurface_locked(currReadSurfHandleFromLoad);
-    fb->unlock();
+    return m_glInfo->postLoadRefreshCurrentContextSurfacePtrs();
 }
diff --git a/stream-servers/RenderThreadInfo.h b/stream-servers/RenderThreadInfo.h
index b291c17..bd45c21 100644
--- a/stream-servers/RenderThreadInfo.h
+++ b/stream-servers/RenderThreadInfo.h
@@ -20,19 +20,10 @@
 #include <memory>
 #include <unordered_set>
 
-#include "RenderContext.h"
-#include "StalePtrRegistry.h"
-#include "SyncThread.h"
-#include "RenderThreadInfoVk.h"
-#include "WindowSurface.h"
 #include "base/Stream.h"
-#include "gles1_dec/GLESv1Decoder.h"
-#include "gles2_dec/GLESv2Decoder.h"
 #include "renderControl_dec/renderControl_dec.h"
-
-typedef uint32_t HandleType;
-typedef std::unordered_set<HandleType> ThreadContextSet;
-typedef std::unordered_set<HandleType> WindowSurfaceSet;
+#include "RenderThreadInfoGl.h"
+#include "RenderThreadInfoVk.h"
 
 // A class used to model the state of each RenderThread related
 struct RenderThreadInfo {
@@ -50,32 +41,15 @@
     // Loop over all active render thread infos
     static void forAllRenderThreadInfos(std::function<void(RenderThreadInfo*)>);
 
-    // Current EGL context, draw surface and read surface.
-    HandleType currContextHandleFromLoad;
-    HandleType currDrawSurfHandleFromLoad;
-    HandleType currReadSurfHandleFromLoad;
+    void initGl();
 
-    RenderContextPtr currContext;
-    WindowSurfacePtr currDrawSurf;
-    WindowSurfacePtr currReadSurf;
-
-    // Decoder states.
-    GLESv1Decoder                   m_glDec;
-    GLESv2Decoder                   m_gl2Dec;
     renderControl_decoder_context_t m_rcDec;
 
-    // All the contexts that are created by this render thread.
-    // New emulator manages contexts in guest process level,
-    // m_contextSet should be deprecated. It is only kept for
-    // backward compatibility reason.
-    ThreadContextSet                m_contextSet;
-    // all the window surfaces that are created by this render thread
-    WindowSurfaceSet                m_windowSet;
-
     // The unique id of owner guest process of this render thread
     uint64_t                        m_puid = 0;
     std::optional<std::string>      m_processName;
 
+    std::optional<RenderThreadInfoGl> m_glInfo;
     std::optional<goldfish_vk::RenderThreadInfoVk> m_vkInfo;
 
     // Functions to save / load a snapshot
diff --git a/stream-servers/RenderThreadInfoGl.cpp b/stream-servers/RenderThreadInfoGl.cpp
new file mode 100644
index 0000000..8f47f17
--- /dev/null
+++ b/stream-servers/RenderThreadInfoGl.cpp
@@ -0,0 +1,130 @@
+// 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 "RenderThreadInfoGl.h"
+
+#include <unordered_set>
+
+#include "FrameBuffer.h"
+#include "OpenGLESDispatch/GLESv1Dispatch.h"
+#include "OpenGLESDispatch/GLESv2Dispatch.h"
+#include "base/Lock.h"
+#include "base/Lookup.h"
+#include "base/StreamSerializing.h"
+#include "host-common/GfxstreamFatalError.h"
+
+using android::base::AutoLock;
+using android::base::Lock;
+using android::base::Stream;
+using emugl::ABORT_REASON_OTHER;
+using emugl::FatalError;
+
+static thread_local RenderThreadInfoGl* tlThreadInfo = nullptr;
+
+RenderThreadInfoGl::RenderThreadInfoGl() {
+    m_glDec.initGL(gles1_dispatch_get_proc_func, nullptr);
+    m_gl2Dec.initGL(gles2_dispatch_get_proc_func, nullptr);
+
+    if (tlThreadInfo != nullptr) {
+      GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
+        << "Attempted to set thread local GL render thread info twice.";
+    }
+    tlThreadInfo = this;
+}
+
+RenderThreadInfoGl::~RenderThreadInfoGl() {
+    tlThreadInfo = nullptr;
+}
+
+RenderThreadInfoGl* RenderThreadInfoGl::get() { return tlThreadInfo; }
+
+void RenderThreadInfoGl::onSave(Stream* stream) {
+    if (currContext) {
+        stream->putBe32(currContext->getHndl());
+    } else {
+        stream->putBe32(0);
+    }
+    if (currDrawSurf) {
+        stream->putBe32(currDrawSurf->getHndl());
+    } else {
+        stream->putBe32(0);
+    }
+    if (currReadSurf) {
+        stream->putBe32(currReadSurf->getHndl());
+    } else {
+        stream->putBe32(0);
+    }
+
+    saveCollection(stream, m_contextSet, [](Stream* stream, HandleType val) {
+        stream->putBe32(val);
+    });
+    saveCollection(stream, m_windowSet, [](Stream* stream, HandleType val) {
+        stream->putBe32(val);
+    });
+
+    stream->putBe64(m_puid);
+
+    // No need to associate render threads with sync threads
+    // if there is a global sync thread. This is only needed
+    // to maintain backward compatibility with snapshot file format.
+    // (Used to be: stream->putBe64(syncThreadAlias))
+    stream->putBe64(0);
+}
+
+bool RenderThreadInfoGl::onLoad(Stream* stream) {
+    FrameBuffer* fb = FrameBuffer::getFB();
+    assert(fb);
+    HandleType ctxHndl = stream->getBe32();
+    HandleType drawSurf = stream->getBe32();
+    HandleType readSurf = stream->getBe32();
+    currContextHandleFromLoad = ctxHndl;
+    currDrawSurfHandleFromLoad = drawSurf;
+    currReadSurfHandleFromLoad = readSurf;
+
+    fb->lock();
+    currContext = fb->getContext_locked(ctxHndl);
+    currDrawSurf = fb->getWindowSurface_locked(drawSurf);
+    currReadSurf = fb->getWindowSurface_locked(readSurf);
+    fb->unlock();
+
+    loadCollection(stream, &m_contextSet, [](Stream* stream) {
+        return stream->getBe32();
+    });
+    loadCollection(stream, &m_windowSet, [](Stream* stream) {
+        return stream->getBe32();
+    });
+
+    m_puid = stream->getBe64();
+
+    // (Used to be: syncThreadAlias = stream->getBe64())
+    stream->getBe64();
+
+    return true;
+}
+
+void RenderThreadInfoGl::postLoadRefreshCurrentContextSurfacePtrs() {
+    FrameBuffer* fb = FrameBuffer::getFB();
+    assert(fb);
+
+    fb->lock();
+    currContext = fb->getContext_locked(currContextHandleFromLoad);
+    currDrawSurf = fb->getWindowSurface_locked(currDrawSurfHandleFromLoad);
+    currReadSurf = fb->getWindowSurface_locked(currReadSurfHandleFromLoad);
+    fb->unlock();
+
+    const HandleType ctx = currContext ? currContext->getHndl() : 0;
+    const HandleType draw = currDrawSurf ? currDrawSurf->getHndl() : 0;
+    const HandleType read = currReadSurf ? currReadSurf->getHndl() : 0;
+    fb->bindContext(ctx, draw, read);
+}
diff --git a/stream-servers/RenderThreadInfoGl.h b/stream-servers/RenderThreadInfoGl.h
new file mode 100644
index 0000000..c1d671b
--- /dev/null
+++ b/stream-servers/RenderThreadInfoGl.h
@@ -0,0 +1,75 @@
+// 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 <optional>
+#include <string>
+
+#include "base/Stream.h"
+#include "gles1_dec/GLESv1Decoder.h"
+#include "gles2_dec/GLESv2Decoder.h"
+#include "RenderContext.h"
+#include "WindowSurface.h"
+#include "StalePtrRegistry.h"
+
+typedef uint32_t HandleType;
+typedef std::unordered_set<HandleType> ThreadContextSet;
+typedef std::unordered_set<HandleType> WindowSurfaceSet;
+
+struct RenderThreadInfoGl {
+    // Create new instance. Only call this once per thread.
+    // Future calls to get() will return this instance until
+    // it is destroyed.
+    RenderThreadInfoGl();
+
+    // Destructor.
+    ~RenderThreadInfoGl();
+
+    // Return the current thread's instance, if any, or NULL.
+    static RenderThreadInfoGl* get();
+
+    // Functions to save / load a snapshot
+    // They must be called after Framebuffer snapshot
+    void onSave(android::base::Stream* stream);
+    bool onLoad(android::base::Stream* stream);
+
+    // Sometimes we can load render thread info before
+    // FrameBuffer repopulates the contexts.
+    void postLoadRefreshCurrentContextSurfacePtrs();
+
+    // The unique id of owner guest process of this render thread
+    uint64_t m_puid = 0;
+
+    // All the contexts that are created by this render thread.
+    // New emulator manages contexts in guest process level,
+    // m_contextSet should be deprecated. It is only kept for
+    // backward compatibility reason.
+    ThreadContextSet                m_contextSet;
+    // all the window surfaces that are created by this render thread
+    WindowSurfaceSet                m_windowSet;
+
+    // Current EGL context, draw surface and read surface.
+    HandleType currContextHandleFromLoad;
+    HandleType currDrawSurfHandleFromLoad;
+    HandleType currReadSurfHandleFromLoad;
+
+    RenderContextPtr currContext;
+    WindowSurfacePtr currDrawSurf;
+    WindowSurfacePtr currReadSurf;
+
+    // Decoder states.
+    GLESv1Decoder                   m_glDec;
+    GLESv2Decoder                   m_gl2Dec;
+};
diff --git a/stream-servers/tests/FrameBuffer_unittest.cpp b/stream-servers/tests/FrameBuffer_unittest.cpp
index d858e43..a3ddec8 100644
--- a/stream-servers/tests/FrameBuffer_unittest.cpp
+++ b/stream-servers/tests/FrameBuffer_unittest.cpp
@@ -104,6 +104,7 @@
         EXPECT_EQ(EGL_SUCCESS, egl->eglGetError());
 
         mRenderThreadInfo = new RenderThreadInfo();
+        mRenderThreadInfo->initGl();
 
         // Snapshots
         mTestSystem.getTempRoot()->makeSubDir("Snapshots");
diff --git a/stream-servers/tests/GLSnapshotTestDispatch.cpp b/stream-servers/tests/GLSnapshotTestDispatch.cpp
index 1e9c192..1b5ed28 100644
--- a/stream-servers/tests/GLSnapshotTestDispatch.cpp
+++ b/stream-servers/tests/GLSnapshotTestDispatch.cpp
@@ -99,11 +99,11 @@
         threadInfo->onLoad(m_stream.get());
         // rebind to context
         fb->bindContext(
-                threadInfo->currContext ? threadInfo->currContext->getHndl()
+                threadInfo->m_glInfo->currContext ? threadInfo->m_glInfo->currContext->getHndl()
                                         : 0,
-                threadInfo->currDrawSurf ? threadInfo->currDrawSurf->getHndl()
+                threadInfo->m_glInfo->currDrawSurf ? threadInfo->m_glInfo->currDrawSurf->getHndl()
                                          : 0,
-                threadInfo->currReadSurf ? threadInfo->currReadSurf->getHndl()
+                threadInfo->m_glInfo->currReadSurf ? threadInfo->m_glInfo->currReadSurf->getHndl()
                                          : 0);
     }
 
diff --git a/stream-servers/tests/SampleApplication.cpp b/stream-servers/tests/SampleApplication.cpp
index e30a5ac..149ffd6 100644
--- a/stream-servers/tests/SampleApplication.cpp
+++ b/stream-servers/tests/SampleApplication.cpp
@@ -263,6 +263,7 @@
     }
 
     mRenderThreadInfo.reset(new RenderThreadInfo());
+    mRenderThreadInfo->initGl();
 
     mColorBuffer = mFb->createColorBuffer(mWidth, mHeight, GL_RGBA, FRAMEWORK_FORMAT_GL_COMPATIBLE);
     mContext = mFb->createRenderContext(0, 0, glVersion);