Bug 709490 - Part 9: Readback without blocking main thread, r=jgilbert

This commit is contained in:
Morris Tseng 2015-09-29 11:51:25 +01:00
parent 2b40efad3d
commit 48052c0b94
18 changed files with 477 additions and 24 deletions

View File

@ -15,13 +15,17 @@
#ifdef XP_WIN #ifdef XP_WIN
#include "nsWindowsHelpers.h" #include "nsWindowsHelpers.h"
#endif #endif
#include "OGLShaderProgram.h"
#include "prenv.h" #include "prenv.h"
#include "GLContext.h" #include "GLContext.h"
#include "GLContextProvider.h"
#include "gfxPrefs.h" #include "gfxPrefs.h"
#include "ScopedGLHelpers.h"
namespace mozilla { namespace mozilla {
namespace gl { namespace gl {
StaticMutex GLLibraryEGL::sMutex;
GLLibraryEGL sEGLLibrary; GLLibraryEGL sEGLLibrary;
#ifdef MOZ_B2G #ifdef MOZ_B2G
ThreadLocal<EGLContext> GLLibraryEGL::sCurrentContext; ThreadLocal<EGLContext> GLLibraryEGL::sCurrentContext;
@ -148,6 +152,32 @@ GetAndInitDisplay(GLLibraryEGL& egl, void* displayType)
return 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 bool
GLLibraryEGL::EnsureInitialized(bool forceAccel) GLLibraryEGL::EnsureInitialized(bool forceAccel)
{ {

View File

@ -10,6 +10,7 @@
#endif #endif
#include "GLLibraryLoader.h" #include "GLLibraryLoader.h"
#include "mozilla/StaticMutex.h"
#include "mozilla/ThreadLocal.h" #include "mozilla/ThreadLocal.h"
#include "nsIFile.h" #include "nsIFile.h"
#include "GeckoProfiler.h" #include "GeckoProfiler.h"
@ -52,6 +53,11 @@
#endif #endif
namespace mozilla { namespace mozilla {
namespace gfx {
class DataSourceSurface;
}
namespace gl { namespace gl {
#undef BEFORE_GL_CALL #undef BEFORE_GL_CALL
@ -94,6 +100,8 @@ namespace gl {
#define AFTER_GL_CALL #define AFTER_GL_CALL
#endif #endif
class GLContext;
class GLLibraryEGL class GLLibraryEGL
{ {
public: public:
@ -478,6 +486,8 @@ public:
return IsExtensionSupported(EXT_create_context_robustness); return IsExtensionSupported(EXT_create_context_robustness);
} }
bool ReadbackEGLImage(EGLImage image, gfx::DataSourceSurface* out_surface);
bool EnsureInitialized(bool forceAccel = false); bool EnsureInitialized(bool forceAccel = false);
void DumpEGLConfig(EGLConfig cfg); void DumpEGLConfig(EGLConfig cfg);
@ -603,9 +613,11 @@ private:
bool mInitialized; bool mInitialized;
PRLibrary* mEGLLibrary; PRLibrary* mEGLLibrary;
EGLDisplay mEGLDisplay; EGLDisplay mEGLDisplay;
RefPtr<GLContext> mReadbackGL;
bool mIsANGLE; bool mIsANGLE;
bool mIsWARP; bool mIsWARP;
static StaticMutex sMutex;
}; };
extern GLLibraryEGL sEGLLibrary; extern GLLibraryEGL sEGLLibrary;

View File

@ -214,7 +214,7 @@ GetActualReadFormats(GLContext* gl,
} }
} }
static void void
SwapRAndBComponents(DataSourceSurface* surf) SwapRAndBComponents(DataSourceSurface* surf)
{ {
DataSourceSurface::MappedSurface map; DataSourceSurface::MappedSurface map;
@ -614,8 +614,7 @@ ReadBackSurface(GLContext* gl, GLuint aTexture, bool aYInvert, SurfaceFormat aFo
#define CLEANUP_IF_GLERROR_OCCURRED(x) \ #define CLEANUP_IF_GLERROR_OCCURRED(x) \
if (DidGLErrorOccur(x)) { \ if (DidGLErrorOccur(x)) { \
isurf = nullptr; \ return false; \
break; \
} }
already_AddRefed<DataSourceSurface> already_AddRefed<DataSourceSurface>
@ -624,6 +623,31 @@ GLReadTexImageHelper::ReadTexImage(GLuint aTextureId,
const gfx::IntSize& aSize, const gfx::IntSize& aSize,
/* ShaderConfigOGL.mFeature */ int aConfig, /* ShaderConfigOGL.mFeature */ int aConfig,
bool aYInvert) 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 || MOZ_ASSERT(aTextureTarget == LOCAL_GL_TEXTURE_2D ||
aTextureTarget == LOCAL_GL_TEXTURE_EXTERNAL || aTextureTarget == LOCAL_GL_TEXTURE_EXTERNAL ||
@ -631,16 +655,6 @@ GLReadTexImageHelper::ReadTexImage(GLuint aTextureId,
mGL->MakeCurrent(); 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; GLint oldrb, oldfb, oldprog, oldTexUnit, oldTex;
GLuint rb, fb; GLuint rb, fb;
@ -737,7 +751,7 @@ GLReadTexImageHelper::ReadTexImage(GLuint aTextureId,
CLEANUP_IF_GLERROR_OCCURRED("when drawing texture"); CLEANUP_IF_GLERROR_OCCURRED("when drawing texture");
/* Read-back draw results */ /* Read-back draw results */
ReadPixelsIntoDataSurface(mGL, isurf); ReadPixelsIntoDataSurface(mGL, aDest);
CLEANUP_IF_GLERROR_OCCURRED("when reading pixels into surface"); CLEANUP_IF_GLERROR_OCCURRED("when reading pixels into surface");
} while (false); } while (false);
@ -756,7 +770,7 @@ GLReadTexImageHelper::ReadTexImage(GLuint aTextureId,
if (oldTexUnit != LOCAL_GL_TEXTURE0) if (oldTexUnit != LOCAL_GL_TEXTURE0)
mGL->fActiveTexture(oldTexUnit); mGL->fActiveTexture(oldTexUnit);
return isurf.forget(); return true;
} }
#undef CLEANUP_IF_GLERROR_OCCURRED #undef CLEANUP_IF_GLERROR_OCCURRED

View File

@ -37,6 +37,9 @@ ReadBackSurface(GLContext* gl, GLuint aTexture, bool aYInvert, gfx::SurfaceForma
already_AddRefed<gfx::DataSourceSurface> already_AddRefed<gfx::DataSourceSurface>
YInvertImageSurface(gfx::DataSourceSurface* aSurf); YInvertImageSurface(gfx::DataSourceSurface* aSurf);
void
SwapRAndBComponents(gfx::DataSourceSurface* surf);
class GLReadTexImageHelper final class GLReadTexImageHelper final
{ {
// The GLContext is the sole owner of the GLBlitHelper. // The GLContext is the sole owner of the GLBlitHelper.
@ -68,12 +71,17 @@ public:
* passed as int to eliminate including LayerManagerOGLProgram.h here. * passed as int to eliminate including LayerManagerOGLProgram.h here.
*/ */
already_AddRefed<gfx::DataSourceSurface> ReadTexImage(GLuint aTextureId, already_AddRefed<gfx::DataSourceSurface> ReadTexImage(GLuint aTextureId,
GLenum aTextureTarget, GLenum aTextureTarget,
const gfx::IntSize& aSize, const gfx::IntSize& aSize,
/* ShaderProgramType */ int aShaderProgram, /* ShaderProgramType */ int aShaderProgram,
bool aYInvert = false); bool aYInvert = false);
bool ReadTexImage(gfx::DataSourceSurface* aDest,
GLuint aTextureId,
GLenum aTextureTarget,
const gfx::IntSize& aSize,
int aShaderProgram,
bool aYInvert = false);
}; };
} // namespace gl } // namespace gl

View File

@ -34,6 +34,7 @@ class nsIThread;
namespace mozilla { namespace mozilla {
namespace gfx { namespace gfx {
class DataSourceSurface;
class DrawTarget; class DrawTarget;
} // namespace gfx } // namespace gfx
@ -200,6 +201,10 @@ public:
} }
virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) = 0; virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) = 0;
virtual bool ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface) {
return false;
}
}; };
template<typename T> template<typename T>

View File

@ -273,6 +273,138 @@ SharedSurface_ANGLEShareHandle::ToSurfaceDescriptor(layers::SurfaceDescriptor* c
return true; 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 // Factory

View File

@ -81,6 +81,8 @@ public:
} }
virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override; virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override;
virtual bool ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface) override;
}; };

View File

@ -10,7 +10,6 @@
#include "GLLibraryEGL.h" #include "GLLibraryEGL.h"
#include "GLReadTexImageHelper.h" #include "GLReadTexImageHelper.h"
#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc #include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc
#include "ScopedGLHelpers.h"
#include "SharedSurface.h" #include "SharedSurface.h"
#include "TextureGarbageBin.h" #include "TextureGarbageBin.h"
@ -213,6 +212,14 @@ SharedSurface_EGLImage::ToSurfaceDescriptor(layers::SurfaceDescriptor* const out
return true; 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> /*static*/ UniquePtr<SurfaceFactory_EGLImage>

View File

@ -79,6 +79,8 @@ public:
void AcquireConsumerTexture(GLContext* consGL, GLuint* out_texture, GLuint* out_target); void AcquireConsumerTexture(GLContext* consGL, GLuint* out_texture, GLuint* out_target);
virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override; virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override;
virtual bool ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface) override;
}; };

View File

@ -9,6 +9,7 @@
#include "GLContextProvider.h" #include "GLContextProvider.h"
#include "GLContextGLX.h" #include "GLContextGLX.h"
#include "GLScreenBuffer.h" #include "GLScreenBuffer.h"
#include "mozilla/gfx/SourceSurfaceCairo.h"
#include "mozilla/layers/LayersSurfaces.h" #include "mozilla/layers/LayersSurfaces.h"
#include "mozilla/layers/ShadowLayerUtilsX11.h" #include "mozilla/layers/ShadowLayerUtilsX11.h"
#include "mozilla/layers/ISurfaceAllocator.h" #include "mozilla/layers/ISurfaceAllocator.h"
@ -83,6 +84,38 @@ SharedSurface_GLXDrawable::ToSurfaceDescriptor(layers::SurfaceDescriptor* const
return true; 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 */ /* static */
UniquePtr<SurfaceFactory_GLXDrawable> UniquePtr<SurfaceFactory_GLXDrawable>
SurfaceFactory_GLXDrawable::Create(GLContext* prodGL, SurfaceFactory_GLXDrawable::Create(GLContext* prodGL,

View File

@ -32,6 +32,8 @@ public:
virtual void UnlockProdImpl() override; virtual void UnlockProdImpl() override;
virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override; virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override;
virtual bool ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface) override;
private: private:
SharedSurface_GLXDrawable(GLContext* gl, SharedSurface_GLXDrawable(GLContext* gl,
const gfx::IntSize& size, const gfx::IntSize& size,

View File

@ -283,5 +283,58 @@ SharedSurface_Gralloc::ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_
return mTextureClient->ToSurfaceDescriptor(*out_descriptor); 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 gl
} // namespace mozilla } // namespace mozilla

View File

@ -75,6 +75,8 @@ public:
} }
virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override; virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override;
virtual bool ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface) override;
}; };
class SurfaceFactory_Gralloc class SurfaceFactory_Gralloc

View File

@ -182,6 +182,31 @@ SharedSurface_IOSurface::ToSurfaceDescriptor(layers::SurfaceDescriptor* const ou
return true; 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 // SurfaceFactory_IOSurface

View File

@ -68,6 +68,8 @@ public:
} }
virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override; virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override;
virtual bool ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface) override;
}; };
class SurfaceFactory_IOSurface : public SurfaceFactory class SurfaceFactory_IOSurface : public SurfaceFactory

View File

@ -12,6 +12,7 @@
#include "GLScreenBuffer.h" #include "GLScreenBuffer.h"
#include "mozilla/dom/HTMLCanvasElement.h" #include "mozilla/dom/HTMLCanvasElement.h"
#include "mozilla/layers/CanvasClient.h" #include "mozilla/layers/CanvasClient.h"
#include "mozilla/layers/TextureClient.h"
#include "mozilla/layers/TextureClientSharedSurface.h" #include "mozilla/layers/TextureClientSharedSurface.h"
#include "mozilla/ReentrantMonitor.h" #include "mozilla/ReentrantMonitor.h"
#include "nsIRunnable.h" #include "nsIRunnable.h"
@ -142,18 +143,119 @@ AsyncCanvasRenderer::GetActiveThread()
return result.forget(); 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> already_AddRefed<gfx::DataSourceSurface>
AsyncCanvasRenderer::UpdateTarget() AsyncCanvasRenderer::UpdateTarget()
{ {
// This function will be implemented in a later patch. if (!mGLContext) {
return nullptr; 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> already_AddRefed<gfx::DataSourceSurface>
AsyncCanvasRenderer::GetSurface() AsyncCanvasRenderer::GetSurface()
{ {
MOZ_ASSERT(NS_IsMainThread()); 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 nsresult

View File

@ -34,6 +34,7 @@ class HTMLCanvasElement;
namespace layers { namespace layers {
class CanvasClient; class CanvasClient;
class TextureClient;
/** /**
* Since HTMLCanvasElement and OffscreenCanvas are not thread-safe, we create * Since HTMLCanvasElement and OffscreenCanvas are not thread-safe, we create
@ -87,6 +88,12 @@ public:
// Can be called in main thread only. // Can be called in main thread only.
already_AddRefed<gfx::DataSourceSurface> GetSurface(); 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 // Readback current WebGL's content and convert it to InputStream. This
// function called GetSurface implicitly and GetSurface handles only get // function called GetSurface implicitly and GetSurface handles only get
// called in the main thread. So this function can be called in main thread. // called in the main thread. So this function can be called in main thread.
@ -142,6 +149,12 @@ private:
// need to protect this member. // need to protect this member.
CanvasClient* mCanvasClient; 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. // Protect non thread-safe objects.
Mutex mMutex; Mutex mMutex;

View File

@ -411,6 +411,15 @@ CanvasClientSharedSurface::UpdateRenderer(gfx::IntSize aSize, Renderer& aRendere
auto layersBackend = shadowForwarder->GetCompositorBackendType(); auto layersBackend = shadowForwarder->GetCompositorBackendType();
mReadbackClient = TexClientFromReadback(surf, forwarder, flags, layersBackend); 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; newFront = mReadbackClient;
} else { } else {
mReadbackClient = nullptr; mReadbackClient = nullptr;