Bug 709490 - Part 9: Readback without blocking main thread, r=jgilbert
☠☠ backed out by c4f66a050ed0 ☠ ☠
authorMorris Tseng <mtseng@mozilla.com>
Tue, 29 Sep 2015 11:51:25 +0100
changeset 287872 ecc38c18734fcb287a7831912906237fdbfb0e6e
parent 287871 22878c936384d27e9429a8481061fa2f15c6c267
child 287873 221385b7b81a520ecc07b2cb54558bd413a55567
push idunknown
push userunknown
push dateunknown
reviewersjgilbert
bugs709490
milestone44.0a1
Bug 709490 - Part 9: Readback without blocking main thread, r=jgilbert
gfx/gl/GLLibraryEGL.cpp
gfx/gl/GLLibraryEGL.h
gfx/gl/GLReadTexImageHelper.cpp
gfx/gl/GLReadTexImageHelper.h
gfx/gl/SharedSurface.h
gfx/gl/SharedSurfaceANGLE.cpp
gfx/gl/SharedSurfaceANGLE.h
gfx/gl/SharedSurfaceEGL.cpp
gfx/gl/SharedSurfaceEGL.h
gfx/gl/SharedSurfaceGLX.cpp
gfx/gl/SharedSurfaceGLX.h
gfx/gl/SharedSurfaceGralloc.cpp
gfx/gl/SharedSurfaceGralloc.h
gfx/gl/SharedSurfaceIO.cpp
gfx/gl/SharedSurfaceIO.h
gfx/layers/AsyncCanvasRenderer.cpp
gfx/layers/AsyncCanvasRenderer.h
gfx/layers/client/CanvasClient.cpp
--- a/gfx/gl/GLLibraryEGL.cpp
+++ b/gfx/gl/GLLibraryEGL.cpp
@@ -10,23 +10,27 @@
 #include "mozilla/Assertions.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsIGfxInfo.h"
 #include "nsPrintfCString.h"
 #ifdef XP_WIN
 #include "nsWindowsHelpers.h"
 #endif
+#include "OGLShaderProgram.h"
 #include "prenv.h"
 #include "GLContext.h"
+#include "GLContextProvider.h"
 #include "gfxPrefs.h"
+#include "ScopedGLHelpers.h"
 
 namespace mozilla {
 namespace gl {
 
+StaticMutex GLLibraryEGL::sMutex;
 GLLibraryEGL sEGLLibrary;
 #ifdef MOZ_B2G
 ThreadLocal<EGLContext> GLLibraryEGL::sCurrentContext;
 #endif
 
 // should match the order of EGLExtensions, and be null-terminated.
 static const char *sEGLExtensionNames[] = {
     "EGL_KHR_image_base",
@@ -144,16 +148,42 @@ GetAndInitDisplay(GLLibraryEGL& egl, voi
 
     if (!egl.fInitialize(display, nullptr, nullptr))
         return EGL_NO_DISPLAY;
 
     return display;
 }
 
 bool
+GLLibraryEGL::ReadbackEGLImage(EGLImage image, gfx::DataSourceSurface* out_surface)
+{
+    StaticMutexAutoUnlock lock(sMutex);
+    if (!mReadbackGL) {
+        mReadbackGL = gl::GLContextProvider::CreateHeadless(gl::CreateContextFlags::NONE);
+    }
+
+    ScopedTexture destTex(mReadbackGL);
+    const GLuint target = LOCAL_GL_TEXTURE_EXTERNAL;
+    ScopedBindTexture autoTex(mReadbackGL, destTex.Texture(), target);
+    mReadbackGL->fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
+    mReadbackGL->fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
+    mReadbackGL->fTexParameteri(target, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_NEAREST);
+    mReadbackGL->fTexParameteri(target, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_NEAREST);
+    mReadbackGL->fEGLImageTargetTexture2D(target, image);
+
+    ShaderConfigOGL config = ShaderConfigFromTargetAndFormat(target,
+                                                             out_surface->GetFormat());
+    int shaderConfig = config.mFeatures;
+    mReadbackGL->ReadTexImageHelper()->ReadTexImage(out_surface, 0, target,
+                                                    out_surface->GetSize(), shaderConfig);
+
+    return true;
+}
+
+bool
 GLLibraryEGL::EnsureInitialized(bool forceAccel)
 {
     if (mInitialized) {
         return true;
     }
 
     mozilla::ScopedGfxFeatureReporter reporter("EGL");
 
--- a/gfx/gl/GLLibraryEGL.h
+++ b/gfx/gl/GLLibraryEGL.h
@@ -5,16 +5,17 @@
 #ifndef GLLIBRARYEGL_H_
 #define GLLIBRARYEGL_H_
 
 #if defined(MOZ_X11)
 #include "mozilla/X11Util.h"
 #endif
 
 #include "GLLibraryLoader.h"
+#include "mozilla/StaticMutex.h"
 #include "mozilla/ThreadLocal.h"
 #include "nsIFile.h"
 #include "GeckoProfiler.h"
 
 #include <bitset>
 #include <vector>
 
 #ifdef XP_WIN
@@ -47,16 +48,21 @@
 
 #if defined(MOZ_X11)
 #define EGL_DEFAULT_DISPLAY  ((EGLNativeDisplayType)mozilla::DefaultXDisplay())
 #else
 #define EGL_DEFAULT_DISPLAY  ((EGLNativeDisplayType)0)
 #endif
 
 namespace mozilla {
+
+namespace gfx {
+class DataSourceSurface;
+}
+
 namespace gl {
 
 #undef BEFORE_GL_CALL
 #undef AFTER_GL_CALL
 
 #ifdef DEBUG
 
 #ifndef MOZ_FUNCTION_NAME
@@ -89,16 +95,18 @@ namespace gl {
 // Record the name of the GL call for better hang stacks on Android.
 #define BEFORE_GL_CALL PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS)
 #else
 #define BEFORE_GL_CALL
 #endif
 #define AFTER_GL_CALL
 #endif
 
+class GLContext;
+
 class GLLibraryEGL
 {
 public:
     GLLibraryEGL()
         : mInitialized(false),
           mEGLLibrary(nullptr),
           mIsANGLE(false),
           mIsWARP(false)
@@ -473,16 +481,18 @@ public:
     bool HasANGLESurfaceD3DTexture2DShareHandle() {
         return IsExtensionSupported(ANGLE_surface_d3d_texture_2d_share_handle);
     }
 
     bool HasRobustness() const {
         return IsExtensionSupported(EXT_create_context_robustness);
     }
 
+    bool ReadbackEGLImage(EGLImage image, gfx::DataSourceSurface* out_surface);
+
     bool EnsureInitialized(bool forceAccel = false);
 
     void DumpEGLConfig(EGLConfig cfg);
     void DumpEGLConfigs();
 
     struct {
         typedef EGLDisplay (GLAPIENTRY * pfnGetDisplay)(void *display_id);
         pfnGetDisplay fGetDisplay;
@@ -598,19 +608,21 @@ public:
     void SetCachedCurrentContext(EGLContext aCtx) { }
     bool CachedCurrentContextMatches() { return true; }
 #endif
 
 private:
     bool mInitialized;
     PRLibrary* mEGLLibrary;
     EGLDisplay mEGLDisplay;
+    RefPtr<GLContext> mReadbackGL;
 
     bool mIsANGLE;
     bool mIsWARP;
+    static StaticMutex sMutex;
 };
 
 extern GLLibraryEGL sEGLLibrary;
 #define EGL_DISPLAY()        sEGLLibrary.Display()
 
 } /* namespace gl */
 } /* namespace mozilla */
 
--- a/gfx/gl/GLReadTexImageHelper.cpp
+++ b/gfx/gl/GLReadTexImageHelper.cpp
@@ -209,17 +209,17 @@ GetActualReadFormats(GLContext* gl,
         return false;
     } else {
         *out_readFormat = destFormat;
         *out_readType = destType;
         return true;
     }
 }
 
-static void
+void
 SwapRAndBComponents(DataSourceSurface* surf)
 {
     DataSourceSurface::MappedSurface map;
     if (!surf->Map(DataSourceSurface::MapType::READ_WRITE, &map)) {
         MOZ_ASSERT(false, "SwapRAndBComponents: Failed to map surface.");
         return;
     }
     MOZ_ASSERT(map.mStride >= 0);
@@ -609,43 +609,57 @@ ReadBackSurface(GLContext* gl, GLuint aT
         surf = YInvertImageSurface(surf);
     }
 
     return surf.forget();
 }
 
 #define CLEANUP_IF_GLERROR_OCCURRED(x)                                      \
     if (DidGLErrorOccur(x)) {                                               \
-        isurf = nullptr;                                                    \
-        break;                                                              \
+        return false;                                                       \
     }
 
 already_AddRefed<DataSourceSurface>
 GLReadTexImageHelper::ReadTexImage(GLuint aTextureId,
                                    GLenum aTextureTarget,
                                    const gfx::IntSize& aSize,
     /* ShaderConfigOGL.mFeature */ int aConfig,
                                    bool aYInvert)
 {
+    /* Allocate resulting image surface */
+    int32_t stride = aSize.width * BytesPerPixel(SurfaceFormat::R8G8B8A8);
+    RefPtr<DataSourceSurface> isurf =
+        Factory::CreateDataSourceSurfaceWithStride(aSize,
+                                                   SurfaceFormat::R8G8B8A8,
+                                                   stride);
+    if (NS_WARN_IF(!isurf)) {
+        return nullptr;
+    }
+
+    if (!ReadTexImage(isurf, aTextureId, aTextureTarget, aSize, aConfig, aYInvert)) {
+        return nullptr;
+    }
+
+    return isurf.forget();
+}
+
+bool
+GLReadTexImageHelper::ReadTexImage(DataSourceSurface* aDest,
+                                   GLuint aTextureId,
+                                   GLenum aTextureTarget,
+                                   const gfx::IntSize& aSize,
+    /* ShaderConfigOGL.mFeature */ int aConfig,
+                                   bool aYInvert)
+{
     MOZ_ASSERT(aTextureTarget == LOCAL_GL_TEXTURE_2D ||
                aTextureTarget == LOCAL_GL_TEXTURE_EXTERNAL ||
                aTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE_ARB);
 
     mGL->MakeCurrent();
 
-    /* Allocate resulting image surface */
-    int32_t stride = aSize.width * BytesPerPixel(SurfaceFormat::R8G8B8A8);
-    RefPtr<DataSourceSurface> isurf =
-        Factory::CreateDataSourceSurfaceWithStride(aSize,
-                                                   SurfaceFormat::R8G8B8A8,
-                                                   stride);
-    if (NS_WARN_IF(!isurf)) {
-        return nullptr;
-    }
-
     GLint oldrb, oldfb, oldprog, oldTexUnit, oldTex;
     GLuint rb, fb;
 
     do {
         mGL->fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING, &oldrb);
         mGL->fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, &oldfb);
         mGL->fGetIntegerv(LOCAL_GL_CURRENT_PROGRAM, &oldprog);
         mGL->fGetIntegerv(LOCAL_GL_ACTIVE_TEXTURE, &oldTexUnit);
@@ -732,17 +746,17 @@ GLReadTexImageHelper::ReadTexImage(GLuin
         mGL->fClearColor(1.0f, 0.0f, 1.0f, 1.0f);
         mGL->fClear(LOCAL_GL_COLOR_BUFFER_BIT);
         CLEANUP_IF_GLERROR_OCCURRED("when clearing color buffer");
 
         mGL->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4);
         CLEANUP_IF_GLERROR_OCCURRED("when drawing texture");
 
         /* Read-back draw results */
-        ReadPixelsIntoDataSurface(mGL, isurf);
+        ReadPixelsIntoDataSurface(mGL, aDest);
         CLEANUP_IF_GLERROR_OCCURRED("when reading pixels into surface");
     } while (false);
 
     /* Restore GL state */
     mGL->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, oldrb);
     mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, oldfb);
     mGL->fUseProgram(oldprog);
 
@@ -751,15 +765,15 @@ GLReadTexImageHelper::ReadTexImage(GLuin
     mGL->fDeleteFramebuffers(1, &fb);
 
     if (aTextureId)
         mGL->fBindTexture(aTextureTarget, oldTex);
 
     if (oldTexUnit != LOCAL_GL_TEXTURE0)
         mGL->fActiveTexture(oldTexUnit);
 
-    return isurf.forget();
+    return true;
 }
 
 #undef CLEANUP_IF_GLERROR_OCCURRED
 
 } // namespace gl
 } // namespace mozilla
--- a/gfx/gl/GLReadTexImageHelper.h
+++ b/gfx/gl/GLReadTexImageHelper.h
@@ -32,16 +32,19 @@ void ReadPixelsIntoDataSurface(GLContext
                                gfx::DataSourceSurface* aSurface);
 
 already_AddRefed<gfx::DataSourceSurface>
 ReadBackSurface(GLContext* gl, GLuint aTexture, bool aYInvert, gfx::SurfaceFormat aFormat);
 
 already_AddRefed<gfx::DataSourceSurface>
 YInvertImageSurface(gfx::DataSourceSurface* aSurf);
 
+void
+SwapRAndBComponents(gfx::DataSourceSurface* surf);
+
 class GLReadTexImageHelper final
 {
     // The GLContext is the sole owner of the GLBlitHelper.
     GLContext* mGL;
 
     GLuint mPrograms[4];
 
     GLuint TextureImageProgramFor(GLenum aTextureTarget, int aShader);
@@ -63,20 +66,25 @@ public:
       * THIS IS EXPENSIVE.  It is ridiculously expensive.  Only do this
       * if you absolutely positively must, and never in any performance
       * critical path.
       *
       * NOTE: aShaderProgram is really mozilla::layers::ShaderProgramType. It is
       * passed as int to eliminate including LayerManagerOGLProgram.h here.
       */
     already_AddRefed<gfx::DataSourceSurface> ReadTexImage(GLuint aTextureId,
-                                                      GLenum aTextureTarget,
-                                                      const gfx::IntSize& aSize,
-                              /* ShaderProgramType */ int aShaderProgram,
-                                                      bool aYInvert = false);
+                                                          GLenum aTextureTarget,
+                                                          const gfx::IntSize& aSize,
+                                  /* ShaderProgramType */ int aShaderProgram,
+                                                          bool aYInvert = false);
 
-
+    bool ReadTexImage(gfx::DataSourceSurface* aDest,
+                      GLuint aTextureId,
+                      GLenum aTextureTarget,
+                      const gfx::IntSize& aSize,
+                      int aShaderProgram,
+                      bool aYInvert = false);
 };
 
 } // namespace gl
 } // namespace mozilla
 
 #endif
--- a/gfx/gl/SharedSurface.h
+++ b/gfx/gl/SharedSurface.h
@@ -29,16 +29,17 @@
 #include "mozilla/WeakPtr.h"
 #include "ScopedGLHelpers.h"
 #include "SurfaceTypes.h"
 
 class nsIThread;
 
 namespace mozilla {
 namespace gfx {
+class DataSourceSurface;
 class DrawTarget;
 } // namespace gfx
 
 namespace layers {
 class ISurfaceAllocator;
 class SharedSurfaceTextureClient;
 enum class TextureFlags : uint32_t;
 class SurfaceDescriptor;
@@ -195,16 +196,20 @@ public:
         return false;
     }
 
     virtual bool NeedsIndirectReads() const {
         return false;
     }
 
     virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) = 0;
+
+    virtual bool ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface) {
+        return false;
+    }
 };
 
 template<typename T>
 class RefSet
 {
     std::set<T*> mSet;
 
 public:
--- a/gfx/gl/SharedSurfaceANGLE.cpp
+++ b/gfx/gl/SharedSurfaceANGLE.cpp
@@ -268,16 +268,148 @@ SharedSurface_ANGLEShareHandle::ToSurfac
 {
     gfx::SurfaceFormat format = mHasAlpha ? gfx::SurfaceFormat::B8G8R8A8
                                           : gfx::SurfaceFormat::B8G8R8X8;
     *out_descriptor = layers::SurfaceDescriptorD3D10((WindowsHandle)mShareHandle, format,
                                                      mSize);
     return true;
 }
 
+class ScopedLockTexture final
+{
+public:
+    explicit ScopedLockTexture(ID3D11Texture2D* texture, bool* succeeded)
+      : mIsLocked(false)
+      , mTexture(texture)
+    {
+        MOZ_ASSERT(mTexture);
+        MOZ_ASSERT(succeeded);
+        *succeeded = false;
+
+        HRESULT hr;
+        mTexture->QueryInterface((IDXGIKeyedMutex**)byRef(mMutex));
+        if (mMutex) {
+            hr = mMutex->AcquireSync(0, 10000);
+            if (hr == WAIT_TIMEOUT) {
+                MOZ_CRASH();
+            }
+
+            if (FAILED(hr)) {
+                NS_WARNING("Failed to lock the texture");
+                return;
+            }
+        }
+
+        ID3D11Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D11Device();
+        device->GetImmediateContext(byRef(mDeviceContext));
+
+        mTexture->GetDesc(&mDesc);
+        mDesc.BindFlags = 0;
+        mDesc.Usage = D3D11_USAGE_STAGING;
+        mDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+        mDesc.MiscFlags = 0;
+
+        hr = device->CreateTexture2D(&mDesc, nullptr, byRef(mCopiedTexture));
+
+        if (FAILED(hr)) {
+            return;
+        }
+
+        mDeviceContext->CopyResource(mCopiedTexture, mTexture);
+
+        hr = mDeviceContext->Map(mCopiedTexture, 0, D3D11_MAP_READ, 0, &mSubresource);
+        if (FAILED(hr)) {
+            return;
+        }
+
+        *succeeded = true;
+        mIsLocked = true;
+    }
+
+    ~ScopedLockTexture()
+    {
+        mDeviceContext->Unmap(mCopiedTexture, 0);
+        if (mMutex) {
+            HRESULT hr = mMutex->ReleaseSync(0);
+            if (FAILED(hr)) {
+                NS_WARNING("Failed to unlock the texture");
+            }
+        }
+        mIsLocked = false;
+    }
+
+    bool mIsLocked;
+    RefPtr<ID3D11Texture2D> mTexture;
+    RefPtr<ID3D11Texture2D> mCopiedTexture;
+    RefPtr<IDXGIKeyedMutex> mMutex;
+    RefPtr<ID3D11DeviceContext> mDeviceContext;
+    D3D11_TEXTURE2D_DESC mDesc;
+    D3D11_MAPPED_SUBRESOURCE mSubresource;
+};
+
+bool
+SharedSurface_ANGLEShareHandle::ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface)
+{
+    MOZ_ASSERT(out_surface);
+    RefPtr<ID3D11Texture2D> tex;
+    ID3D11Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D11Device();
+    HRESULT hr = device->OpenSharedResource(mShareHandle,
+                                            __uuidof(ID3D11Texture2D),
+                                            (void**)(ID3D11Texture2D**)byRef(tex));
+
+    if (FAILED(hr)) {
+        return false;
+    }
+
+    bool succeeded = false;
+    ScopedLockTexture scopedLock(tex, &succeeded);
+    if (!succeeded) {
+        return false;
+    }
+
+    const uint8_t* data = reinterpret_cast<uint8_t*>(scopedLock.mSubresource.pData);
+    uint32_t srcStride = scopedLock.mSubresource.RowPitch;
+
+    gfx::DataSourceSurface::ScopedMap map(out_surface, gfx::DataSourceSurface::WRITE);
+    if (!map.IsMapped()) {
+        return false;
+    }
+
+    if (map.GetStride() == srcStride) {
+        memcpy(map.GetData(), data, out_surface->GetSize().height * map.GetStride());
+    } else {
+        const uint8_t bytesPerPixel = BytesPerPixel(out_surface->GetFormat());
+        for (int32_t i = 0; i < out_surface->GetSize().height; i++) {
+            memcpy(map.GetData() + i * map.GetStride(),
+                   data + i * srcStride,
+                   bytesPerPixel * out_surface->GetSize().width);
+        }
+    }
+
+    DXGI_FORMAT srcFormat = scopedLock.mDesc.Format;
+    MOZ_ASSERT(srcFormat == DXGI_FORMAT_B8G8R8A8_UNORM ||
+               srcFormat == DXGI_FORMAT_B8G8R8X8_UNORM ||
+               srcFormat == DXGI_FORMAT_R8G8B8A8_UNORM);
+    bool isSrcRGB = srcFormat == DXGI_FORMAT_R8G8B8A8_UNORM;
+
+    gfx::SurfaceFormat destFormat = out_surface->GetFormat();
+    MOZ_ASSERT(destFormat == gfx::SurfaceFormat::R8G8B8X8 ||
+               destFormat == gfx::SurfaceFormat::R8G8B8A8 ||
+               destFormat == gfx::SurfaceFormat::B8G8R8X8 ||
+               destFormat == gfx::SurfaceFormat::B8G8R8A8);
+    bool isDestRGB = destFormat == gfx::SurfaceFormat::R8G8B8X8 ||
+                     destFormat == gfx::SurfaceFormat::R8G8B8A8;
+
+    if (isSrcRGB != isDestRGB) {
+        SwapRAndBComponents(out_surface);
+    }
+
+    return true;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Factory
 
 /*static*/ UniquePtr<SurfaceFactory_ANGLEShareHandle>
 SurfaceFactory_ANGLEShareHandle::Create(GLContext* gl, const SurfaceCaps& caps,
                                         const RefPtr<layers::ISurfaceAllocator>& allocator,
                                         const layers::TextureFlags& flags)
 {
--- a/gfx/gl/SharedSurfaceANGLE.h
+++ b/gfx/gl/SharedSurfaceANGLE.h
@@ -76,16 +76,18 @@ public:
     virtual bool WaitSync_ContentThread_Impl() override;
     virtual bool PollSync_ContentThread_Impl() override;
 
     const RefPtr<ID3D11Texture2D>& GetConsumerTexture() const {
         return mConsumerTexture;
     }
 
     virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override;
+
+    virtual bool ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface) override;
 };
 
 
 
 class SurfaceFactory_ANGLEShareHandle
     : public SurfaceFactory
 {
 protected:
--- a/gfx/gl/SharedSurfaceEGL.cpp
+++ b/gfx/gl/SharedSurfaceEGL.cpp
@@ -5,17 +5,16 @@
 
 #include "SharedSurfaceEGL.h"
 
 #include "GLBlitHelper.h"
 #include "GLContextEGL.h"
 #include "GLLibraryEGL.h"
 #include "GLReadTexImageHelper.h"
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor, etc
-#include "ScopedGLHelpers.h"
 #include "SharedSurface.h"
 #include "TextureGarbageBin.h"
 
 namespace mozilla {
 namespace gl {
 
 /*static*/ UniquePtr<SharedSurface_EGLImage>
 SharedSurface_EGLImage::Create(GLContext* prodGL,
@@ -208,16 +207,24 @@ SharedSurface_EGLImage::AcquireConsumerT
 bool
 SharedSurface_EGLImage::ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor)
 {
     *out_descriptor = layers::EGLImageDescriptor((uintptr_t)mImage, (uintptr_t)mSync,
                                                  mSize, mHasAlpha);
     return true;
 }
 
+bool
+SharedSurface_EGLImage::ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface)
+{
+    MOZ_ASSERT(out_surface);
+    MOZ_ASSERT(NS_IsMainThread());
+    return sEGLLibrary.ReadbackEGLImage(mImage, out_surface);
+}
+
 ////////////////////////////////////////////////////////////////////////
 
 /*static*/ UniquePtr<SurfaceFactory_EGLImage>
 SurfaceFactory_EGLImage::Create(GLContext* prodGL, const SurfaceCaps& caps,
                                 const RefPtr<layers::ISurfaceAllocator>& allocator,
                                 const layers::TextureFlags& flags)
 {
     EGLContext context = GLContextEGL::Cast(prodGL)->mContext;
--- a/gfx/gl/SharedSurfaceEGL.h
+++ b/gfx/gl/SharedSurfaceEGL.h
@@ -74,16 +74,18 @@ public:
       return mProdTex;
     }
 
     // Implementation-specific functions below:
     // Returns texture and target
     void AcquireConsumerTexture(GLContext* consGL, GLuint* out_texture, GLuint* out_target);
 
     virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override;
+
+    virtual bool ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface) override;
 };
 
 
 
 class SurfaceFactory_EGLImage
     : public SurfaceFactory
 {
 public:
--- a/gfx/gl/SharedSurfaceGLX.cpp
+++ b/gfx/gl/SharedSurfaceGLX.cpp
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "SharedSurfaceGLX.h"
 #include "gfxXlibSurface.h"
 #include "GLXLibrary.h"
 #include "GLContextProvider.h"
 #include "GLContextGLX.h"
 #include "GLScreenBuffer.h"
+#include "mozilla/gfx/SourceSurfaceCairo.h"
 #include "mozilla/layers/LayersSurfaces.h"
 #include "mozilla/layers/ShadowLayerUtilsX11.h"
 #include "mozilla/layers/ISurfaceAllocator.h"
 #include "mozilla/X11Util.h"
 
 namespace mozilla {
 namespace gl {
 
@@ -78,16 +79,48 @@ SharedSurface_GLXDrawable::ToSurfaceDesc
 {
   if (!mXlibSurface)
       return false;
 
    *out_descriptor = layers::SurfaceDescriptorX11(mXlibSurface, mInSameProcess);
    return true;
 }
 
+bool
+SharedSurface_GLXDrawable::ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface)
+{
+    MOZ_ASSERT(out_surface);
+    RefPtr<gfx::DataSourceSurface> dataSurf =
+        new gfx::DataSourceSurfaceCairo(mXlibSurface->CairoSurface());
+
+    gfx::DataSourceSurface::ScopedMap mapSrc(dataSurf, gfx::DataSourceSurface::READ);
+    if (!mapSrc.IsMapped()) {
+        return false;
+    }
+
+    gfx::DataSourceSurface::ScopedMap mapDest(out_surface, gfx::DataSourceSurface::WRITE);
+    if (!mapDest.IsMapped()) {
+        return false;
+    }
+
+    if (mapDest.GetStride() == mapSrc.GetStride()) {
+        memcpy(mapDest.GetData(),
+               mapSrc.GetData(),
+               out_surface->GetSize().height * mapDest.GetStride());
+    } else {
+        for (int32_t i = 0; i < dataSurf->GetSize().height; i++) {
+            memcpy(mapDest.GetData() + i * mapDest.GetStride(),
+                   mapSrc.GetData() + i * mapSrc.GetStride(),
+                   std::min(mapSrc.GetStride(), mapDest.GetStride()));
+        }
+    }
+
+    return true;
+}
+
 /* static */
 UniquePtr<SurfaceFactory_GLXDrawable>
 SurfaceFactory_GLXDrawable::Create(GLContext* prodGL,
                                    const SurfaceCaps& caps,
                                    const RefPtr<layers::ISurfaceAllocator>& allocator,
                                    const layers::TextureFlags& flags)
 {
     MOZ_ASSERT(caps.alpha, "GLX surfaces require an alpha channel!");
--- a/gfx/gl/SharedSurfaceGLX.h
+++ b/gfx/gl/SharedSurfaceGLX.h
@@ -27,16 +27,18 @@ public:
     virtual void Fence() override;
     virtual bool WaitSync() override { return true; }
     virtual bool PollSync() override { return true; }
 
     virtual void LockProdImpl() override;
     virtual void UnlockProdImpl() override;
 
     virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override;
+
+    virtual bool ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface) override;
 private:
     SharedSurface_GLXDrawable(GLContext* gl,
                               const gfx::IntSize& size,
                               bool inSameProcess,
                               const RefPtr<gfxXlibSurface>& xlibSurface);
 
     RefPtr<gfxXlibSurface> mXlibSurface;
     bool mInSameProcess;
--- a/gfx/gl/SharedSurfaceGralloc.cpp
+++ b/gfx/gl/SharedSurfaceGralloc.cpp
@@ -278,10 +278,63 @@ SharedSurface_Gralloc::WaitForBufferOwne
 
 bool
 SharedSurface_Gralloc::ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor)
 {
     mTextureClient->MarkShared();
     return mTextureClient->ToSurfaceDescriptor(*out_descriptor);
 }
 
+bool
+SharedSurface_Gralloc::ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface)
+{
+    MOZ_ASSERT(out_surface);
+    sp<GraphicBuffer> buffer = mTextureClient->GetGraphicBuffer();
+
+    const uint8_t* grallocData = nullptr;
+    auto result = buffer->lock(
+        GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_NEVER,
+        const_cast<void**>(reinterpret_cast<const void**>(&grallocData))
+    );
+
+    if (result == BAD_VALUE) {
+        return false;
+    }
+
+    gfx::DataSourceSurface::ScopedMap map(out_surface, gfx::DataSourceSurface::WRITE);
+    if (!map.IsMapped()) {
+        buffer->unlock();
+        return false;
+    }
+
+    uint32_t stride = buffer->getStride() * android::bytesPerPixel(buffer->getPixelFormat());
+    uint32_t height = buffer->getHeight();
+    uint32_t width = buffer->getWidth();
+    for (uint32_t i = 0; i < height; i++) {
+        memcpy(map.GetData() + i * map.GetStride(),
+               grallocData + i * stride, width * 4);
+    }
+
+    buffer->unlock();
+
+    android::PixelFormat srcFormat = buffer->getPixelFormat();
+    MOZ_ASSERT(srcFormat == PIXEL_FORMAT_RGBA_8888 ||
+               srcFormat == PIXEL_FORMAT_BGRA_8888 ||
+               srcFormat == PIXEL_FORMAT_RGBX_8888);
+    bool isSrcRGB = srcFormat == PIXEL_FORMAT_RGBA_8888 ||
+                    srcFormat == PIXEL_FORMAT_RGBX_8888;
+
+    gfx::SurfaceFormat destFormat = out_surface->GetFormat();
+    MOZ_ASSERT(destFormat == gfx::SurfaceFormat::R8G8B8X8 ||
+               destFormat == gfx::SurfaceFormat::R8G8B8A8 ||
+               destFormat == gfx::SurfaceFormat::B8G8R8X8 ||
+               destFormat == gfx::SurfaceFormat::B8G8R8A8);
+    bool isDestRGB = destFormat == gfx::SurfaceFormat::R8G8B8X8 ||
+                     destFormat == gfx::SurfaceFormat::R8G8B8A8;
+
+    if (isSrcRGB != isDestRGB) {
+        SwapRAndBComponents(out_surface);
+    }
+    return true;
+}
+
 } // namespace gl
 } // namespace mozilla
--- a/gfx/gl/SharedSurfaceGralloc.h
+++ b/gfx/gl/SharedSurfaceGralloc.h
@@ -70,16 +70,18 @@ public:
         return mProdTex;
     }
 
     layers::GrallocTextureClientOGL* GetTextureClient() {
         return mTextureClient;
     }
 
     virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override;
+
+    virtual bool ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface) override;
 };
 
 class SurfaceFactory_Gralloc
     : public SurfaceFactory
 {
 public:
     SurfaceFactory_Gralloc(GLContext* prodGL, const SurfaceCaps& caps,
                            const RefPtr<layers::ISurfaceAllocator>& allocator,
--- a/gfx/gl/SharedSurfaceIO.cpp
+++ b/gfx/gl/SharedSurfaceIO.cpp
@@ -177,16 +177,41 @@ SharedSurface_IOSurface::ToSurfaceDescri
 {
     bool isOpaque = !mHasAlpha;
     *out_descriptor = layers::SurfaceDescriptorMacIOSurface(mIOSurf->GetIOSurfaceID(),
                                                             mIOSurf->GetContentsScaleFactor(),
                                                             isOpaque);
     return true;
 }
 
+bool
+SharedSurface_IOSurface::ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface)
+{
+    MOZ_ASSERT(out_surface);
+    mIOSurf->Lock();
+    size_t bytesPerRow = mIOSurf->GetBytesPerRow();
+    size_t ioWidth = mIOSurf->GetDevicePixelWidth();
+    size_t ioHeight = mIOSurf->GetDevicePixelHeight();
+
+    const unsigned char* ioData = (unsigned char*)mIOSurf->GetBaseAddress();
+    gfx::DataSourceSurface::ScopedMap map(out_surface, gfx::DataSourceSurface::WRITE);
+    if (!map.IsMapped()) {
+        mIOSurf->Unlock();
+        return false;
+    }
+
+    for (size_t i = 0; i < ioHeight; i++) {
+        memcpy(map.GetData() + i * map.GetStride(),
+               ioData + i * bytesPerRow, ioWidth * 4);
+    }
+
+    mIOSurf->Unlock();
+    return true;
+}
+
 ////////////////////////////////////////////////////////////////////////
 // SurfaceFactory_IOSurface
 
 /*static*/ UniquePtr<SurfaceFactory_IOSurface>
 SurfaceFactory_IOSurface::Create(GLContext* gl, const SurfaceCaps& caps,
                                  const RefPtr<layers::ISurfaceAllocator>& allocator,
                                  const layers::TextureFlags& flags)
 {
--- a/gfx/gl/SharedSurfaceIO.h
+++ b/gfx/gl/SharedSurfaceIO.h
@@ -63,16 +63,18 @@ public:
         return mIOSurf;
     }
 
     virtual bool NeedsIndirectReads() const override {
         return true;
     }
 
     virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override;
+
+    virtual bool ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface) override;
 };
 
 class SurfaceFactory_IOSurface : public SurfaceFactory
 {
 public:
     // Infallible.
     static UniquePtr<SurfaceFactory_IOSurface> Create(GLContext* gl,
                                                       const SurfaceCaps& caps,
--- a/gfx/layers/AsyncCanvasRenderer.cpp
+++ b/gfx/layers/AsyncCanvasRenderer.cpp
@@ -7,16 +7,17 @@
 #include "AsyncCanvasRenderer.h"
 
 #include "gfxUtils.h"
 #include "GLContext.h"
 #include "GLReadTexImageHelper.h"
 #include "GLScreenBuffer.h"
 #include "mozilla/dom/HTMLCanvasElement.h"
 #include "mozilla/layers/CanvasClient.h"
+#include "mozilla/layers/TextureClient.h"
 #include "mozilla/layers/TextureClientSharedSurface.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "nsIRunnable.h"
 #include "nsThreadUtils.h"
 
 namespace mozilla {
 namespace layers {
 
@@ -137,28 +138,129 @@ AsyncCanvasRenderer::ResetActiveThread()
 already_AddRefed<nsIThread>
 AsyncCanvasRenderer::GetActiveThread()
 {
   MutexAutoLock lock(mMutex);
   nsCOMPtr<nsIThread> result = mActiveThread;
   return result.forget();
 }
 
+void
+AsyncCanvasRenderer::CopyFromTextureClient(TextureClient* aTextureClient)
+{
+  MutexAutoLock lock(mMutex);
+  RefPtr<BufferTextureClient> buffer = static_cast<BufferTextureClient*>(aTextureClient);
+  if (!buffer->Lock(layers::OpenMode::OPEN_READ)) {
+    return;
+  }
+
+  const gfx::IntSize& size = aTextureClient->GetSize();
+  // This buffer would be used later for content rendering. So we choose
+  // B8G8R8A8 format here.
+  const gfx::SurfaceFormat format = gfx::SurfaceFormat::B8G8R8A8;
+  // Avoid to create buffer every time.
+  if (!mSurfaceForBasic ||
+      size != mSurfaceForBasic->GetSize() ||
+      format != mSurfaceForBasic->GetFormat())
+  {
+    uint32_t stride = gfx::GetAlignedStride<8>(size.width * BytesPerPixel(format));
+    mSurfaceForBasic = gfx::Factory::CreateDataSourceSurfaceWithStride(size, format, stride);
+  }
+
+  const uint8_t* lockedBytes = buffer->GetLockedData();
+  gfx::DataSourceSurface::ScopedMap map(mSurfaceForBasic,
+                                        gfx::DataSourceSurface::MapType::WRITE);
+  if (!map.IsMapped()) {
+    buffer->Unlock();
+    return;
+  }
+
+  memcpy(map.GetData(), lockedBytes, map.GetStride() * mSurfaceForBasic->GetSize().height);
+  buffer->Unlock();
+
+  if (mSurfaceForBasic->GetFormat() == gfx::SurfaceFormat::R8G8B8A8 ||
+      mSurfaceForBasic->GetFormat() == gfx::SurfaceFormat::R8G8B8X8) {
+    gl::SwapRAndBComponents(mSurfaceForBasic);
+  }
+}
+
 already_AddRefed<gfx::DataSourceSurface>
 AsyncCanvasRenderer::UpdateTarget()
 {
-  // This function will be implemented in a later patch.
-  return nullptr;
+  if (!mGLContext) {
+    return nullptr;
+  }
+
+  gl::SharedSurface* frontbuffer = nullptr;
+  gl::GLScreenBuffer* screen = mGLContext->Screen();
+  const auto& front = screen->Front();
+  if (front) {
+    frontbuffer = front->Surf();
+  }
+
+  if (!frontbuffer) {
+    return nullptr;
+  }
+
+  if (frontbuffer->mType == gl::SharedSurfaceType::Basic) {
+    return nullptr;
+  }
+
+  const gfx::IntSize& size = frontbuffer->mSize;
+  // This buffer would be used later for content rendering. So we choose
+  // B8G8R8A8 format here.
+  const gfx::SurfaceFormat format = gfx::SurfaceFormat::B8G8R8A8;
+  uint32_t stride = gfx::GetAlignedStride<8>(size.width * BytesPerPixel(format));
+  RefPtr<gfx::DataSourceSurface> surface =
+    gfx::Factory::CreateDataSourceSurfaceWithStride(size, format, stride);
+
+
+  if (NS_WARN_IF(!surface)) {
+    return nullptr;
+  }
+
+  if (!frontbuffer->ReadbackBySharedHandle(surface)) {
+    return nullptr;
+  }
+
+  bool needsPremult = frontbuffer->mHasAlpha && !mIsAlphaPremultiplied;
+  if (needsPremult) {
+    gfxUtils::PremultiplyDataSurface(surface, surface);
+  }
+
+  return surface.forget();
 }
 
 already_AddRefed<gfx::DataSourceSurface>
 AsyncCanvasRenderer::GetSurface()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  return UpdateTarget();
+  MutexAutoLock lock(mMutex);
+  if (mSurfaceForBasic) {
+    // Since SourceSurface isn't thread-safe, we need copy to a new SourceSurface.
+    RefPtr<gfx::DataSourceSurface> result =
+      gfx::Factory::CreateDataSourceSurfaceWithStride(mSurfaceForBasic->GetSize(),
+                                                      mSurfaceForBasic->GetFormat(),
+                                                      mSurfaceForBasic->Stride());
+
+    gfx::DataSourceSurface::ScopedMap srcMap(mSurfaceForBasic, gfx::DataSourceSurface::READ);
+    gfx::DataSourceSurface::ScopedMap dstMap(result, gfx::DataSourceSurface::WRITE);
+
+    if (NS_WARN_IF(!srcMap.IsMapped()) ||
+        NS_WARN_IF(!dstMap.IsMapped())) {
+      return nullptr;
+    }
+
+    memcpy(dstMap.GetData(),
+           srcMap.GetData(),
+           srcMap.GetStride() * mSurfaceForBasic->GetSize().height);
+    return result.forget();
+  } else {
+    return UpdateTarget();
+  }
 }
 
 nsresult
 AsyncCanvasRenderer::GetInputStream(const char *aMimeType,
                                     const char16_t *aEncoderOptions,
                                     nsIInputStream **aStream)
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/gfx/layers/AsyncCanvasRenderer.h
+++ b/gfx/layers/AsyncCanvasRenderer.h
@@ -29,16 +29,17 @@ class GLContext;
 
 namespace dom {
 class HTMLCanvasElement;
 }
 
 namespace layers {
 
 class CanvasClient;
+class TextureClient;
 
 /**
  * Since HTMLCanvasElement and OffscreenCanvas are not thread-safe, we create
  * AsyncCanvasRenderer which is thread-safe wrapper object for communicating
  * among main, worker and ImageBridgeChild threads.
  *
  * Each HTMLCanvasElement object is responsible for creating
  * AsyncCanvasRenderer object. Once Canvas is transfered to worker,
@@ -82,16 +83,22 @@ public:
   void SetActiveThread();
   void ResetActiveThread();
 
   // This will readback surface and return the surface
   // in the DataSourceSurface.
   // Can be called in main thread only.
   already_AddRefed<gfx::DataSourceSurface> GetSurface();
 
+  // For SharedSurface_Basic case, before the frame sending to the compositor,
+  // we readback it to a texture client because SharedSurface_Basic cannot shared.
+  // We don't want to readback it again here, so just copy the content of that
+  // texture client here to avoid readback again.
+  void CopyFromTextureClient(TextureClient *aClient);
+
   // Readback current WebGL's content and convert it to InputStream. This
   // function called GetSurface implicitly and GetSurface handles only get
   // called in the main thread. So this function can be called in main thread.
   nsresult
   GetInputStream(const char *aMimeType,
                  const char16_t *aEncoderOptions,
                  nsIInputStream **aStream);
 
@@ -137,16 +144,22 @@ private:
   uint64_t mCanvasClientAsyncID;
 
   // The lifetime of this pointer is controlled by OffscreenCanvas
   // Can be accessed in active thread and ImageBridge thread.
   // But we never accessed it at the same time on both thread. So no
   // need to protect this member.
   CanvasClient* mCanvasClient;
 
+  // When backend is LAYER_BASIC and SharedSurface type is Basic.
+  // CanvasClient will readback the GLContext to a TextureClient
+  // in order to send frame to compositor. To avoid readback again,
+  // we copy from this TextureClient to this mSurfaceForBasic directly
+  // by calling CopyFromTextureClient().
+  RefPtr<gfx::DataSourceSurface> mSurfaceForBasic;
 
   // Protect non thread-safe objects.
   Mutex mMutex;
 
   // Can be accessed in any thread, need protect by mutex.
   nsCOMPtr<nsIThread> mActiveThread;
 };
 
--- a/gfx/layers/client/CanvasClient.cpp
+++ b/gfx/layers/client/CanvasClient.cpp
@@ -406,16 +406,25 @@ CanvasClientSharedSurface::UpdateRendere
       MOZ_ASSERT(asyncRenderer);
       flags |= mTextureFlags;
       shadowForwarder = GetForwarder();
     }
 
     auto layersBackend = shadowForwarder->GetCompositorBackendType();
     mReadbackClient = TexClientFromReadback(surf, forwarder, flags, layersBackend);
 
+    if (asyncRenderer) {
+      // Above codes will readback the GLContext to mReadbackClient
+      // in order to send frame to compositor. We copy from this
+      // TextureClient directly by calling CopyFromTextureClient().
+      // Therefore, if main-thread want the content of GLContext,
+      // it don't have to readback it again.
+      asyncRenderer->CopyFromTextureClient(mReadbackClient);
+    }
+
     newFront = mReadbackClient;
   } else {
     mReadbackClient = nullptr;
   }
 
   MOZ_ASSERT(newFront);
   if (!newFront) {
     // May happen in a release build in case of memory pressure.