Bug 875232 - Workaround glReadPixels being broken with framebuffers backed by an IOSurface by copying to a temporary texture first. r=jgilbert

This commit is contained in:
Matt Woodrow 2013-07-17 23:24:15 -04:00
parent 9653ce195e
commit 1806ba3946
6 changed files with 106 additions and 1 deletions

View File

@ -15,6 +15,7 @@
#include <ctype.h>
#include <set>
#include <stack>
#include <map>
#ifdef WIN32
#include <windows.h>
@ -718,7 +719,16 @@ public:
y = FixYValue(y, height);
BeforeGLReadCall();
raw_fReadPixels(x, y, width, height, format, type, pixels);
bool didReadPixels = false;
if (mScreen) {
didReadPixels = mScreen->ReadPixels(x, y, width, height, format, type, pixels);
}
if (!didReadPixels) {
raw_fReadPixels(x, y, width, height, format, type, pixels);
}
AfterGLReadCall();
}
@ -1182,6 +1192,8 @@ protected:
int32_t mRenderer;
public:
std::map<GLuint, SharedSurface_GL*> mFBOMapping;
enum {
DebugEnabled = 1 << 0,
DebugTrace = 1 << 1,
@ -3183,6 +3195,33 @@ protected:
}
};
struct ScopedTexture
: public ScopedGLWrapper<ScopedTexture>
{
friend struct ScopedGLWrapper<ScopedTexture>;
protected:
GLuint mTexture;
public:
ScopedTexture(GLContext* gl)
: ScopedGLWrapper<ScopedTexture>(gl)
{
mGL->fGenTextures(1, &mTexture);
}
GLuint Texture() { return mTexture; }
protected:
void UnwrapImpl() {
// Check that we're not falling out of scope after
// the current context changed.
MOZ_ASSERT(mGL->IsCurrent());
mGL->fDeleteTextures(1, &mTexture);
}
};
struct ScopedBindTexture
: public ScopedGLWrapper<ScopedBindTexture>
{

View File

@ -288,6 +288,28 @@ GLScreenBuffer::BeforeReadCall()
AssureBlitted();
}
bool
GLScreenBuffer::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height,
GLenum format, GLenum type, GLvoid *pixels)
{
// If the currently bound framebuffer is backed by a SharedSurface_GL
// then it might want to override how we read pixel data from it.
// This is normally only the default framebuffer, but we can also
// have SharedSurfaces bound to other framebuffers when doing
// readback for BasicLayers.
SharedSurface_GL* surf;
if (GetReadFB() == 0) {
surf = SharedSurf();
} else {
surf = mGL->mFBOMapping[GetReadFB()];
}
if (surf) {
return surf->ReadPixels(x, y, width, height, format, type, pixels);
}
return false;
}
void
GLScreenBuffer::RequireBlit()
{
@ -593,6 +615,7 @@ ReadBuffer::Create(GLContext* gl,
GLuint fb = 0;
gl->fGenFramebuffers(1, &fb);
gl->AttachBuffersToFB(colorTex, colorRB, depthRB, stencilRB, fb, target);
gl->mFBOMapping[fb] = surf;
MOZ_ASSERT(gl->IsFramebufferComplete(fb));
@ -613,6 +636,7 @@ ReadBuffer::~ReadBuffer()
mGL->fDeleteFramebuffers(1, &fb);
mGL->fDeleteRenderbuffers(2, rbs);
mGL->mFBOMapping.erase(mFB);
}
void
@ -641,6 +665,7 @@ ReadBuffer::Attach(SharedSurface_GL* surf)
}
mGL->AttachBuffersToFB(colorTex, colorRB, 0, 0, mFB, target);
mGL->mFBOMapping[mFB] = surf;
MOZ_ASSERT(mGL->IsFramebufferComplete(mFB));
}

View File

@ -244,6 +244,16 @@ public:
void AfterDrawCall();
void BeforeReadCall();
/**
* Attempts to read pixels from the current bound framebuffer, if
* it is backed by a SharedSurface_GL.
*
* Returns true if the pixel data has been read back, false
* otherwise.
*/
bool ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height,
GLenum format, GLenum type, GLvoid *pixels);
/* Morph swaps out our SurfaceStream mechanism and replaces it with
* one best suited to our platform and compositor configuration.
*

View File

@ -59,6 +59,11 @@ public:
return (SharedSurface_GL*)surf;
}
virtual bool ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height,
GLenum format, GLenum type, GLvoid *pixels) {
return false;
}
virtual void LockProd();
virtual void UnlockProd();

View File

@ -27,6 +27,29 @@ SharedSurface_IOSurface::Fence()
mGL->fFlush();
}
bool
SharedSurface_IOSurface::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height,
GLenum format, GLenum type, GLvoid *pixels)
{
// Calling glReadPixels when an IOSurface is bound to the current framebuffer
// can cause corruption in following glReadPixel calls (even if they aren't
// reading from an IOSurface).
// We workaround this by copying to a temporary texture, and doing the readback
// from that.
ScopedTexture texture(mGL);
ScopedBindTexture bindTex(mGL, texture.Texture());
mGL->fCopyTexImage2D(LOCAL_GL_TEXTURE_2D, 0,
HasAlpha() ? LOCAL_GL_RGBA : LOCAL_GL_RGB,
x, y,
width, height, 0);
ScopedFramebufferForTexture fb(mGL, texture.Texture());
ScopedBindFramebuffer bindFB(mGL, fb.FB());
mGL->fReadPixels(0, 0, width, height, format, type, pixels);
return true;
}
SharedSurface_IOSurface::SharedSurface_IOSurface(MacIOSurface* surface,
GLContext* gl,
const gfxIntSize& size,

View File

@ -27,6 +27,9 @@ public:
virtual void Fence();
virtual bool WaitSync() { return true; }
virtual bool ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height,
GLenum format, GLenum type, GLvoid *pixels) MOZ_OVERRIDE;
virtual GLuint Texture() const
{
return mTexture;