diff --git a/gfx/gl/GLLibraryEGL.cpp b/gfx/gl/GLLibraryEGL.cpp index 4f2a715699b..a78be99d7f1 100644 --- a/gfx/gl/GLLibraryEGL.cpp +++ b/gfx/gl/GLLibraryEGL.cpp @@ -15,13 +15,17 @@ #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 GLLibraryEGL::sCurrentContext; @@ -148,6 +152,32 @@ GetAndInitDisplay(GLLibraryEGL& egl, void* displayType) 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) { diff --git a/gfx/gl/GLLibraryEGL.h b/gfx/gl/GLLibraryEGL.h index 37ccc3d9d23..1f42a951a75 100644 --- a/gfx/gl/GLLibraryEGL.h +++ b/gfx/gl/GLLibraryEGL.h @@ -10,6 +10,7 @@ #endif #include "GLLibraryLoader.h" +#include "mozilla/StaticMutex.h" #include "mozilla/ThreadLocal.h" #include "nsIFile.h" #include "GeckoProfiler.h" @@ -52,6 +53,11 @@ #endif namespace mozilla { + +namespace gfx { +class DataSourceSurface; +} + namespace gl { #undef BEFORE_GL_CALL @@ -94,6 +100,8 @@ namespace gl { #define AFTER_GL_CALL #endif +class GLContext; + class GLLibraryEGL { public: @@ -478,6 +486,8 @@ public: return IsExtensionSupported(EXT_create_context_robustness); } + bool ReadbackEGLImage(EGLImage image, gfx::DataSourceSurface* out_surface); + bool EnsureInitialized(bool forceAccel = false); void DumpEGLConfig(EGLConfig cfg); @@ -603,9 +613,11 @@ private: bool mInitialized; PRLibrary* mEGLLibrary; EGLDisplay mEGLDisplay; + RefPtr mReadbackGL; bool mIsANGLE; bool mIsWARP; + static StaticMutex sMutex; }; extern GLLibraryEGL sEGLLibrary; diff --git a/gfx/gl/GLReadTexImageHelper.cpp b/gfx/gl/GLReadTexImageHelper.cpp index 0efea4c92ee..7e43d8b350b 100644 --- a/gfx/gl/GLReadTexImageHelper.cpp +++ b/gfx/gl/GLReadTexImageHelper.cpp @@ -214,7 +214,7 @@ GetActualReadFormats(GLContext* gl, } } -static void +void SwapRAndBComponents(DataSourceSurface* surf) { DataSourceSurface::MappedSurface map; @@ -614,8 +614,7 @@ ReadBackSurface(GLContext* gl, GLuint aTexture, bool aYInvert, SurfaceFormat aFo #define CLEANUP_IF_GLERROR_OCCURRED(x) \ if (DidGLErrorOccur(x)) { \ - isurf = nullptr; \ - break; \ + return false; \ } already_AddRefed @@ -624,6 +623,31 @@ GLReadTexImageHelper::ReadTexImage(GLuint aTextureId, const gfx::IntSize& aSize, /* ShaderConfigOGL.mFeature */ int aConfig, bool aYInvert) +{ + /* Allocate resulting image surface */ + int32_t stride = aSize.width * BytesPerPixel(SurfaceFormat::R8G8B8A8); + RefPtr 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 || @@ -631,16 +655,6 @@ GLReadTexImageHelper::ReadTexImage(GLuint aTextureId, mGL->MakeCurrent(); - /* Allocate resulting image surface */ - int32_t stride = aSize.width * BytesPerPixel(SurfaceFormat::R8G8B8A8); - RefPtr isurf = - Factory::CreateDataSourceSurfaceWithStride(aSize, - SurfaceFormat::R8G8B8A8, - stride); - if (NS_WARN_IF(!isurf)) { - return nullptr; - } - GLint oldrb, oldfb, oldprog, oldTexUnit, oldTex; GLuint rb, fb; @@ -737,7 +751,7 @@ GLReadTexImageHelper::ReadTexImage(GLuint aTextureId, 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); @@ -756,7 +770,7 @@ GLReadTexImageHelper::ReadTexImage(GLuint aTextureId, if (oldTexUnit != LOCAL_GL_TEXTURE0) mGL->fActiveTexture(oldTexUnit); - return isurf.forget(); + return true; } #undef CLEANUP_IF_GLERROR_OCCURRED diff --git a/gfx/gl/GLReadTexImageHelper.h b/gfx/gl/GLReadTexImageHelper.h index 7adc4edba48..685afcf270c 100644 --- a/gfx/gl/GLReadTexImageHelper.h +++ b/gfx/gl/GLReadTexImageHelper.h @@ -37,6 +37,9 @@ ReadBackSurface(GLContext* gl, GLuint aTexture, bool aYInvert, gfx::SurfaceForma already_AddRefed YInvertImageSurface(gfx::DataSourceSurface* aSurf); +void +SwapRAndBComponents(gfx::DataSourceSurface* surf); + class GLReadTexImageHelper final { // The GLContext is the sole owner of the GLBlitHelper. @@ -68,12 +71,17 @@ public: * passed as int to eliminate including LayerManagerOGLProgram.h here. */ already_AddRefed 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 diff --git a/gfx/gl/SharedSurface.h b/gfx/gl/SharedSurface.h index 217cac05b23..0e98689acf0 100644 --- a/gfx/gl/SharedSurface.h +++ b/gfx/gl/SharedSurface.h @@ -34,6 +34,7 @@ class nsIThread; namespace mozilla { namespace gfx { +class DataSourceSurface; class DrawTarget; } // namespace gfx @@ -200,6 +201,10 @@ public: } virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) = 0; + + virtual bool ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface) { + return false; + } }; template diff --git a/gfx/gl/SharedSurfaceANGLE.cpp b/gfx/gl/SharedSurfaceANGLE.cpp index 381ba8ee8dd..9b8c444f6d7 100644 --- a/gfx/gl/SharedSurfaceANGLE.cpp +++ b/gfx/gl/SharedSurfaceANGLE.cpp @@ -273,6 +273,138 @@ SharedSurface_ANGLEShareHandle::ToSurfaceDescriptor(layers::SurfaceDescriptor* c 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 mTexture; + RefPtr mCopiedTexture; + RefPtr mMutex; + RefPtr mDeviceContext; + D3D11_TEXTURE2D_DESC mDesc; + D3D11_MAPPED_SUBRESOURCE mSubresource; +}; + +bool +SharedSurface_ANGLEShareHandle::ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface) +{ + MOZ_ASSERT(out_surface); + RefPtr 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(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 diff --git a/gfx/gl/SharedSurfaceANGLE.h b/gfx/gl/SharedSurfaceANGLE.h index 3dcab9d0433..f37b50ed988 100644 --- a/gfx/gl/SharedSurfaceANGLE.h +++ b/gfx/gl/SharedSurfaceANGLE.h @@ -81,6 +81,8 @@ public: } virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override; + + virtual bool ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface) override; }; diff --git a/gfx/gl/SharedSurfaceEGL.cpp b/gfx/gl/SharedSurfaceEGL.cpp index ca3f18dfb29..f4391cd0fbd 100644 --- a/gfx/gl/SharedSurfaceEGL.cpp +++ b/gfx/gl/SharedSurfaceEGL.cpp @@ -10,7 +10,6 @@ #include "GLLibraryEGL.h" #include "GLReadTexImageHelper.h" #include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc -#include "ScopedGLHelpers.h" #include "SharedSurface.h" #include "TextureGarbageBin.h" @@ -213,6 +212,14 @@ SharedSurface_EGLImage::ToSurfaceDescriptor(layers::SurfaceDescriptor* const out 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 diff --git a/gfx/gl/SharedSurfaceEGL.h b/gfx/gl/SharedSurfaceEGL.h index 0184887e001..7dd847ef6f1 100644 --- a/gfx/gl/SharedSurfaceEGL.h +++ b/gfx/gl/SharedSurfaceEGL.h @@ -79,6 +79,8 @@ public: 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; }; diff --git a/gfx/gl/SharedSurfaceGLX.cpp b/gfx/gl/SharedSurfaceGLX.cpp index ecf8320ff7c..e91630c22ea 100644 --- a/gfx/gl/SharedSurfaceGLX.cpp +++ b/gfx/gl/SharedSurfaceGLX.cpp @@ -9,6 +9,7 @@ #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" @@ -83,6 +84,38 @@ SharedSurface_GLXDrawable::ToSurfaceDescriptor(layers::SurfaceDescriptor* const return true; } +bool +SharedSurface_GLXDrawable::ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface) +{ + MOZ_ASSERT(out_surface); + RefPtr 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::Create(GLContext* prodGL, diff --git a/gfx/gl/SharedSurfaceGLX.h b/gfx/gl/SharedSurfaceGLX.h index 1822fba4d71..528c223400b 100644 --- a/gfx/gl/SharedSurfaceGLX.h +++ b/gfx/gl/SharedSurfaceGLX.h @@ -32,6 +32,8 @@ public: 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, diff --git a/gfx/gl/SharedSurfaceGralloc.cpp b/gfx/gl/SharedSurfaceGralloc.cpp index ba957c40127..87c285ca135 100644 --- a/gfx/gl/SharedSurfaceGralloc.cpp +++ b/gfx/gl/SharedSurfaceGralloc.cpp @@ -283,5 +283,58 @@ SharedSurface_Gralloc::ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_ return mTextureClient->ToSurfaceDescriptor(*out_descriptor); } +bool +SharedSurface_Gralloc::ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface) +{ + MOZ_ASSERT(out_surface); + sp buffer = mTextureClient->GetGraphicBuffer(); + + const uint8_t* grallocData = nullptr; + auto result = buffer->lock( + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_NEVER, + const_cast(reinterpret_cast(&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 diff --git a/gfx/gl/SharedSurfaceGralloc.h b/gfx/gl/SharedSurfaceGralloc.h index 14097224cde..d19e33c461d 100644 --- a/gfx/gl/SharedSurfaceGralloc.h +++ b/gfx/gl/SharedSurfaceGralloc.h @@ -75,6 +75,8 @@ public: } virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override; + + virtual bool ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface) override; }; class SurfaceFactory_Gralloc diff --git a/gfx/gl/SharedSurfaceIO.cpp b/gfx/gl/SharedSurfaceIO.cpp index e50b8d8f0c3..2154327daf3 100644 --- a/gfx/gl/SharedSurfaceIO.cpp +++ b/gfx/gl/SharedSurfaceIO.cpp @@ -182,6 +182,31 @@ SharedSurface_IOSurface::ToSurfaceDescriptor(layers::SurfaceDescriptor* const ou 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 diff --git a/gfx/gl/SharedSurfaceIO.h b/gfx/gl/SharedSurfaceIO.h index 3976b5787f0..fe787ef9cf1 100644 --- a/gfx/gl/SharedSurfaceIO.h +++ b/gfx/gl/SharedSurfaceIO.h @@ -68,6 +68,8 @@ public: } virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override; + + virtual bool ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface) override; }; class SurfaceFactory_IOSurface : public SurfaceFactory diff --git a/gfx/layers/AsyncCanvasRenderer.cpp b/gfx/layers/AsyncCanvasRenderer.cpp index 755da24a6c9..8c7ed428cf8 100644 --- a/gfx/layers/AsyncCanvasRenderer.cpp +++ b/gfx/layers/AsyncCanvasRenderer.cpp @@ -12,6 +12,7 @@ #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" @@ -142,18 +143,119 @@ AsyncCanvasRenderer::GetActiveThread() return result.forget(); } +void +AsyncCanvasRenderer::CopyFromTextureClient(TextureClient* aTextureClient) +{ + MutexAutoLock lock(mMutex); + RefPtr buffer = static_cast(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 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 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 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 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 diff --git a/gfx/layers/AsyncCanvasRenderer.h b/gfx/layers/AsyncCanvasRenderer.h index 993669141be..6a84b070352 100644 --- a/gfx/layers/AsyncCanvasRenderer.h +++ b/gfx/layers/AsyncCanvasRenderer.h @@ -34,6 +34,7 @@ class HTMLCanvasElement; namespace layers { class CanvasClient; +class TextureClient; /** * Since HTMLCanvasElement and OffscreenCanvas are not thread-safe, we create @@ -87,6 +88,12 @@ public: // Can be called in main thread only. already_AddRefed 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. @@ -142,6 +149,12 @@ private: // 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 mSurfaceForBasic; // Protect non thread-safe objects. Mutex mMutex; diff --git a/gfx/layers/client/CanvasClient.cpp b/gfx/layers/client/CanvasClient.cpp index 765c9e17b55..5b722c63aaa 100644 --- a/gfx/layers/client/CanvasClient.cpp +++ b/gfx/layers/client/CanvasClient.cpp @@ -411,6 +411,15 @@ CanvasClientSharedSurface::UpdateRenderer(gfx::IntSize aSize, Renderer& aRendere 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;