Bug 1444432 - Create a Compositor abstraction called AsyncReadbackBuffer and implement it for CompositorOGL. r?jrmuizel
MozReview-Commit-ID: Jx1RFFoKypz
--- a/gfx/layers/Compositor.h
+++ b/gfx/layers/Compositor.h
@@ -130,16 +130,17 @@ class DataTextureSource;
class CompositingRenderTarget;
class CompositorBridgeParent;
class LayerManagerComposite;
class CompositorOGL;
class CompositorD3D11;
class BasicCompositor;
class TextureReadLock;
struct GPUStats;
+class AsyncReadbackBuffer;
enum SurfaceInitMode
{
INIT_MODE_NONE,
INIT_MODE_CLEAR
};
/**
@@ -258,16 +259,33 @@ public:
* aSourcePoint specifies the point in aSource to copy data from.
*/
virtual already_AddRefed<CompositingRenderTarget>
CreateRenderTargetFromSource(const gfx::IntRect& aRect,
const CompositingRenderTarget* aSource,
const gfx::IntPoint& aSourcePoint) = 0;
/**
+ * Grab a snapshot of aSource and store it in aDest, so that the pixels can
+ * be read on the CPU by mapping aDest at some point in the future.
+ * aSource and aDest must have the same size.
+ * If this is a GPU compositor, this call must not block on the GPU.
+ * Returns whether the operation was successful.
+ */
+ virtual bool
+ ReadbackRenderTarget(CompositingRenderTarget* aSource,
+ AsyncReadbackBuffer* aDest) { return false; }
+
+ /**
+ * Create an AsyncReadbackBuffer of the specified size. Can return null.
+ */
+ virtual already_AddRefed<AsyncReadbackBuffer>
+ CreateAsyncReadbackBuffer(const gfx::IntSize& aSize) { return nullptr; }
+
+ /**
* Sets the given surface as the target for subsequent calls to DrawQuad.
* Passing null as aSurface sets the screen as the target.
*/
virtual void SetRenderTarget(CompositingRenderTarget* aSurface) = 0;
/**
* Returns the current target for rendering. Will return null if we are
* rendering to the screen.
@@ -620,16 +638,32 @@ BlendOpIsMixBlendMode(gfx::CompositionOp
case gfx::CompositionOp::OP_COLOR:
case gfx::CompositionOp::OP_LUMINOSITY:
return true;
default:
return false;
}
}
+class AsyncReadbackBuffer
+{
+public:
+ NS_INLINE_DECL_REFCOUNTING(AsyncReadbackBuffer)
+
+ gfx::IntSize GetSize() const { return mSize; }
+ virtual bool MapAndCopyInto(gfx::DataSourceSurface* aSurface,
+ const gfx::IntSize& aReadSize) const=0;
+
+protected:
+ explicit AsyncReadbackBuffer(const gfx::IntSize& aSize) : mSize(aSize) {}
+ virtual ~AsyncReadbackBuffer() {}
+
+ gfx::IntSize mSize;
+};
+
struct TexturedVertex
{
float position[2];
float texCoords[2];
};
nsTArray<TexturedVertex>
TexturedTrianglesToVertexArray(const nsTArray<gfx::TexturedTriangle>& aTriangles);
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -39,16 +39,17 @@
#include "nsMathUtils.h" // for NS_roundf
#include "nsRect.h" // for mozilla::gfx::IntRect
#include "nsServiceManagerUtils.h" // for do_GetService
#include "nsString.h" // for nsString, nsAutoCString, etc
#include "ScopedGLHelpers.h"
#include "GLReadTexImageHelper.h"
#include "GLBlitTextureImageHelper.h"
#include "HeapCopyOfStackArray.h"
+#include "mozilla/gfx/Swizzle.h"
#if MOZ_WIDGET_ANDROID
#include "GeneratedJNIWrappers.h"
#endif
#include "GeckoProfiler.h"
namespace mozilla {
@@ -58,16 +59,97 @@ using namespace gfx;
namespace layers {
using namespace mozilla::gl;
static const GLuint kCoordinateAttributeIndex = 0;
static const GLuint kTexCoordinateAttributeIndex = 1;
+class AsyncReadbackBufferOGL final : public AsyncReadbackBuffer
+{
+public:
+ AsyncReadbackBufferOGL(GLContext* aGL, const IntSize& aSize);
+
+ bool MapAndCopyInto(DataSourceSurface* aSurface,
+ const IntSize& aReadSize) const override;
+
+ void Bind() const
+ {
+ mGL->fBindBuffer(LOCAL_GL_PIXEL_PACK_BUFFER, mBufferHandle);
+ mGL->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, 1);
+ }
+
+protected:
+ ~AsyncReadbackBufferOGL() override;
+
+private:
+ GLContext* mGL;
+ GLuint mBufferHandle;
+};
+
+AsyncReadbackBufferOGL::AsyncReadbackBufferOGL(GLContext* aGL,
+ const IntSize& aSize)
+ : AsyncReadbackBuffer(aSize)
+ , mGL(aGL)
+{
+ size_t bufferByteCount = mSize.width * mSize.height * 4;
+ mGL->fGenBuffers(1, &mBufferHandle);
+
+ ScopedPackState scopedPackState(mGL);
+ Bind();
+ mGL->fBufferData(LOCAL_GL_PIXEL_PACK_BUFFER, bufferByteCount, nullptr,
+ LOCAL_GL_STREAM_READ);
+}
+
+AsyncReadbackBufferOGL::~AsyncReadbackBufferOGL()
+{
+ if (mGL && mGL->MakeCurrent()) {
+ mGL->fDeleteBuffers(1, &mBufferHandle);
+ }
+}
+
+bool
+AsyncReadbackBufferOGL::MapAndCopyInto(DataSourceSurface* aSurface,
+ const IntSize& aReadSize) const
+{
+ MOZ_RELEASE_ASSERT(aReadSize <= aSurface->GetSize());
+
+ if (!mGL || !mGL->MakeCurrent()) {
+ return false;
+ }
+
+ ScopedPackState scopedPackState(mGL);
+ Bind();
+ uint8_t* srcData = static_cast<uint8_t*>(
+ mGL->fMapBuffer(LOCAL_GL_PIXEL_PACK_BUFFER, LOCAL_GL_READ_ONLY));
+
+ if (!srcData) {
+ return false;
+ }
+
+ int32_t srcStride = mSize.width * 4; // Bind() sets an alignment of 1
+ DataSourceSurface::ScopedMap map(aSurface, DataSourceSurface::WRITE);
+ uint8_t* destData = map.GetData();
+ int32_t destStride = map.GetStride();
+ SurfaceFormat destFormat = aSurface->GetFormat();
+ for (int32_t destRow = 0; destRow < aReadSize.height; destRow++) {
+ // Turn srcData upside down during the copy.
+ int32_t srcRow = aReadSize.height - 1 - destRow;
+ uint8_t* src = &srcData[srcRow * srcStride];
+ uint8_t* dest = &destData[destRow * destStride];
+ SwizzleData(src, srcStride, SurfaceFormat::R8G8B8A8,
+ dest, destStride, destFormat, IntSize(aReadSize.width, 1));
+ }
+
+ mGL->fUnmapBuffer(LOCAL_GL_PIXEL_PACK_BUFFER);
+
+ return true;
+}
+
static void
BindMaskForProgram(ShaderProgramOGL* aProgram, TextureSourceOGL* aSourceMask,
GLenum aTexUnit, const gfx::Matrix4x4& aTransform)
{
MOZ_ASSERT(LOCAL_GL_TEXTURE0 <= aTexUnit && aTexUnit <= LOCAL_GL_TEXTURE31);
aSourceMask->BindTexture(aTexUnit, gfx::SamplingFilter::LINEAR);
aProgram->SetMaskTextureUnit(aTexUnit - LOCAL_GL_TEXTURE0);
aProgram->SetMaskLayerTransform(aTransform);
@@ -598,16 +680,46 @@ CompositorOGL::SetRenderTarget(Compositi
}
CompositingRenderTarget*
CompositorOGL::GetCurrentRenderTarget() const
{
return mCurrentRenderTarget;
}
+already_AddRefed<AsyncReadbackBuffer>
+CompositorOGL::CreateAsyncReadbackBuffer(const IntSize& aSize)
+{
+ return MakeAndAddRef<AsyncReadbackBufferOGL>(mGLContext, aSize);
+}
+
+bool
+CompositorOGL::ReadbackRenderTarget(CompositingRenderTarget* aSource,
+ AsyncReadbackBuffer* aDest)
+{
+ IntSize size = aSource->GetSize();
+ MOZ_RELEASE_ASSERT(aDest->GetSize() == size);
+
+ RefPtr<CompositingRenderTarget> previousTarget = GetCurrentRenderTarget();
+ if (previousTarget != aSource) {
+ SetRenderTarget(aSource);
+ }
+
+ ScopedPackState scopedPackState(mGLContext);
+ static_cast<AsyncReadbackBufferOGL*>(aDest)->Bind();
+
+ mGLContext->fReadPixels(0, 0, size.width, size.height,
+ LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, 0);
+
+ if (previousTarget != aSource) {
+ SetRenderTarget(previousTarget);
+ }
+ return true;
+}
+
static GLenum
GetFrameBufferInternalFormat(GLContext* gl,
GLuint aFrameBuffer,
mozilla::widget::CompositorWidget* aWidget)
{
if (aFrameBuffer == 0) { // default framebuffer
return aWidget->GetGLFrameBufferFormat();
}
--- a/gfx/layers/opengl/CompositorOGL.h
+++ b/gfx/layers/opengl/CompositorOGL.h
@@ -153,16 +153,23 @@ public:
virtual already_AddRefed<CompositingRenderTarget>
CreateRenderTargetFromSource(const gfx::IntRect &aRect,
const CompositingRenderTarget *aSource,
const gfx::IntPoint &aSourcePoint) override;
virtual void SetRenderTarget(CompositingRenderTarget *aSurface) override;
virtual CompositingRenderTarget* GetCurrentRenderTarget() const override;
+ virtual bool
+ ReadbackRenderTarget(CompositingRenderTarget* aSource,
+ AsyncReadbackBuffer* aDest) override;
+
+ virtual already_AddRefed<AsyncReadbackBuffer>
+ CreateAsyncReadbackBuffer(const gfx::IntSize& aSize) override;
+
virtual void DrawQuad(const gfx::Rect& aRect,
const gfx::IntRect& aClipRect,
const EffectChain &aEffectChain,
gfx::Float aOpacity,
const gfx::Matrix4x4& aTransform,
const gfx::Rect& aVisibleRect) override;
virtual void DrawTriangles(const nsTArray<gfx::TexturedTriangle>& aTriangles,