Bug 615976 - Adds support for separate Draw/Read buffers in GLContext, with lazy blitting - r=bjacob

This commit is contained in:
Jeff Gilbert 2011-10-19 15:09:57 -04:00
parent ea51ff68ce
commit eb17b5e0b2
3 changed files with 320 additions and 33 deletions

View File

@ -377,6 +377,21 @@ GLContext::InitWithPrefix(const char *prefix, bool trygl)
(mSymbols.fMapBuffer && mSymbols.fUnmapBuffer),
"ARB_pixel_buffer_object supported without glMapBuffer/UnmapBuffer being available!");
// Check for aux symbols based on extensions
if (IsExtensionSupported(GLContext::ANGLE_framebuffer_blit) ||
IsExtensionSupported(GLContext::EXT_framebuffer_blit)) {
SymLoadStruct auxSymbols[] = {
{ (PRFuncPtr*) &mSymbols.fBlitFramebuffer, { "BlitFramebuffer", "BlitFramebufferEXT", "BlitFramebufferANGLE", NULL } },
{ NULL, { NULL } },
};
if (!LoadSymbols(&auxSymbols[0], trygl, prefix)) {
NS_RUNTIMEABORT("GL supports framebuffer_blit without supplying glBlitFramebuffer");
mInitialized = false;
}
}
}
if (mInitialized) {
GLint v[4];
fGetIntegerv(LOCAL_GL_SCISSOR_BOX, v);
@ -438,6 +453,8 @@ static const char *sExtensionNames[] = {
"GL_ARB_texture_float",
"GL_EXT_unpack_subimage",
"GL_OES_standard_derivatives",
"GL_EXT_framebuffer_blit",
"GL_ANGLE_framebuffer_blit",
NULL
};
@ -997,11 +1014,12 @@ GLContext::ResizeOffscreenFBO(const gfxIntSize& aSize)
const int depth = mCreationFormat.depth;
const int stencil = mCreationFormat.stencil;
const bool firstTime = (mOffscreenFBO == 0);
const bool firstTime = (mOffscreenDrawFBO == 0 && mOffscreenReadFBO == 0);
GLuint curBoundTexture = 0;
GLuint curBoundFramebufferDraw = 0;
GLuint curBoundFramebufferRead = 0;
GLuint curBoundRenderbuffer = 0;
GLuint curBoundFramebuffer = 0;
GLuint curBoundTexture = 0;
GLint viewport[4];
@ -1009,9 +1027,10 @@ GLContext::ResizeOffscreenFBO(const gfxIntSize& aSize)
!mIsGLES2 || IsExtensionSupported(OES_packed_depth_stencil);
// save a few things for later restoring
fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, (GLint*) &curBoundFramebuffer);
fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, (GLint*) &curBoundTexture);
curBoundFramebufferDraw = GetBoundDrawFBO();
curBoundFramebufferRead = GetBoundReadFBO();
fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING, (GLint*) &curBoundRenderbuffer);
fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, (GLint*) &curBoundTexture);
fGetIntegerv(LOCAL_GL_VIEWPORT, viewport);
// the context format of what we're defining
@ -1020,13 +1039,15 @@ GLContext::ResizeOffscreenFBO(const gfxIntSize& aSize)
// Create everything we need for the resize, so if it fails, we haven't broken anything
// If successful, these new resized objects will replace their associated member vars in GLContext
GLuint newOffscreenFBO = 0;
GLuint newOffscreenDrawFBO = 0;
GLuint newOffscreenReadFBO = 0;
GLuint newOffscreenTexture = 0;
GLuint newOffscreenDepthRB = 0;
GLuint newOffscreenStencilRB = 0;
fGenFramebuffers(1, &newOffscreenFBO);
fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, newOffscreenFBO);
fGenFramebuffers(1, &newOffscreenReadFBO);
newOffscreenDrawFBO = newOffscreenReadFBO;
fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, newOffscreenReadFBO);
fGenTextures(1, &newOffscreenTexture);
fBindTexture(LOCAL_GL_TEXTURE_2D, newOffscreenTexture);
@ -1155,17 +1176,41 @@ GLContext::ResizeOffscreenFBO(const gfxIntSize& aSize)
}
// We should be all resized. Check for framebuffer completeness.
GLenum status = fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
GLenum status;
bool framebuffersComplete = true;
fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, newOffscreenDrawFBO);
status = fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
NS_WARNING("Error resizing offscreen framebuffer -- framebuffer not complete");
NS_WARNING("DrawFBO: Incomplete");
#ifdef DEBUG
printf_stderr("Framebuffer status: %X\n", status);
#endif
framebuffersComplete = false;
}
fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, newOffscreenReadFBO);
status = fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
NS_WARNING("ReadFBO: Incomplete");
#ifdef DEBUG
printf_stderr("Framebuffer status: %X\n", status);
#endif
framebuffersComplete = false;
}
if (!framebuffersComplete) {
NS_WARNING("Error resizing offscreen framebuffer -- framebuffer(s) not complete");
// Clean up the mess
fDeleteFramebuffers(1, &newOffscreenFBO);
fDeleteFramebuffers(1, &newOffscreenDrawFBO);
fDeleteFramebuffers(1, &newOffscreenReadFBO);
fDeleteTextures(1, &newOffscreenTexture);
fDeleteRenderbuffers(1, &newOffscreenDepthRB);
fDeleteRenderbuffers(1, &newOffscreenStencilRB);
fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, curBoundFramebuffer);
BindReadFBO(curBoundFramebufferRead);
BindDrawFBO(curBoundFramebufferDraw);
fBindTexture(LOCAL_GL_TEXTURE_2D, curBoundTexture);
fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, curBoundRenderbuffer);
fViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
@ -1174,15 +1219,18 @@ GLContext::ResizeOffscreenFBO(const gfxIntSize& aSize)
}
// Success, so delete the old and busted
fDeleteFramebuffers(1, &mOffscreenFBO);
fDeleteFramebuffers(1, &mOffscreenDrawFBO);
fDeleteFramebuffers(1, &mOffscreenReadFBO);
fDeleteTextures(1, &mOffscreenTexture);
fDeleteRenderbuffers(1, &mOffscreenDepthRB);
fDeleteRenderbuffers(1, &mOffscreenStencilRB);
// Update currently bound references if we're changing what they were point to
// This way we don't rebind to old buffers when we're done here
if (curBoundFramebuffer == mOffscreenFBO)
curBoundFramebuffer = newOffscreenFBO;
if (curBoundFramebufferDraw == mOffscreenDrawFBO)
curBoundFramebufferDraw = newOffscreenDrawFBO;
if (curBoundFramebufferRead == mOffscreenReadFBO)
curBoundFramebufferRead = newOffscreenReadFBO;
if (curBoundTexture == mOffscreenTexture)
curBoundTexture = newOffscreenTexture;
if (curBoundRenderbuffer == mOffscreenDepthRB)
@ -1191,7 +1239,8 @@ GLContext::ResizeOffscreenFBO(const gfxIntSize& aSize)
curBoundRenderbuffer = newOffscreenStencilRB;
// Replace with the new hotness
mOffscreenFBO = newOffscreenFBO;
mOffscreenDrawFBO = newOffscreenDrawFBO;
mOffscreenReadFBO = newOffscreenReadFBO;
mOffscreenTexture = newOffscreenTexture;
mOffscreenDepthRB = newOffscreenDepthRB;
mOffscreenStencilRB = newOffscreenStencilRB;
@ -1203,7 +1252,9 @@ GLContext::ResizeOffscreenFBO(const gfxIntSize& aSize)
#ifdef DEBUG
if (mDebugMode) {
printf_stderr("Created offscreen FBO: r: %d g: %d b: %d a: %d depth: %d stencil: %d\n",
printf_stderr("%s %dx%d offscreen FBO: r: %d g: %d b: %d a: %d depth: %d stencil: %d\n",
firstTime ? "Created" : "Resized",
mOffscreenActualSize.width, mOffscreenActualSize.height,
mActualFormat.red, mActualFormat.green, mActualFormat.blue, mActualFormat.alpha,
mActualFormat.depth, mActualFormat.stencil);
}
@ -1215,12 +1266,16 @@ GLContext::ResizeOffscreenFBO(const gfxIntSize& aSize)
// can restore them.
fViewport(0, 0, aSize.width, aSize.height);
// Make sure we know that the buffers are new and thus dirty:
ForceDirtyFBOs();
// Clear the new framebuffer with the full viewport
fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mOffscreenFBO);
fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, GetOffscreenFBO());
ClearSafely();
// Ok, now restore the GL state back to what it was before the resize took place.
fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, curBoundFramebuffer);
BindDrawFBO(curBoundFramebufferDraw);
BindReadFBO(curBoundFramebufferRead);
fBindTexture(LOCAL_GL_TEXTURE_2D, curBoundTexture);
fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, curBoundRenderbuffer);
@ -1235,12 +1290,14 @@ GLContext::ResizeOffscreenFBO(const gfxIntSize& aSize)
void
GLContext::DeleteOffscreenFBO()
{
fDeleteFramebuffers(1, &mOffscreenFBO);
fDeleteFramebuffers(1, &mOffscreenDrawFBO);
fDeleteFramebuffers(1, &mOffscreenReadFBO);
fDeleteTextures(1, &mOffscreenTexture);
fDeleteRenderbuffers(1, &mOffscreenDepthRB);
fDeleteRenderbuffers(1, &mOffscreenStencilRB);
mOffscreenFBO = 0;
mOffscreenDrawFBO = 0;
mOffscreenReadFBO = 0;
mOffscreenTexture = 0;
mOffscreenDepthRB = 0;
mOffscreenStencilRB = 0;

View File

@ -547,7 +547,9 @@ public:
mFlipped(false),
mBlitProgram(0),
mBlitFramebuffer(0),
mOffscreenFBO(0),
mOffscreenDrawFBO(0),
mOffscreenReadFBO(0),
mOffscreenFBOsDirty(false),
mOffscreenDepthRB(0),
mOffscreenStencilRB(0)
#ifdef DEBUG
@ -719,7 +721,7 @@ public:
return false;
}
if (!aOffscreen->mOffscreenFBO) {
if (!aOffscreen->mOffscreenDrawFBO && !aOffscreen->mOffscreenReadFBO) {
return false;
}
@ -749,7 +751,7 @@ public:
* Only valid if IsOffscreen() returns true.
*/
virtual bool ResizeOffscreen(const gfxIntSize& aNewSize) {
if (mOffscreenFBO)
if (mOffscreenDrawFBO || mOffscreenReadFBO)
return ResizeOffscreenFBO(aNewSize);
return false;
}
@ -781,12 +783,228 @@ public:
* Only valid if IsOffscreen() returns true.
*/
GLuint GetOffscreenFBO() {
return mOffscreenFBO;
// 0 is interpreted as (off)screen, whether for read or draw operations
return 0;
}
GLuint GetOffscreenTexture() {
return mOffscreenTexture;
}
virtual bool SupportsOffscreenSplit() {
return IsExtensionSupported(EXT_framebuffer_blit) || IsExtensionSupported(ANGLE_framebuffer_blit);
}
GLuint GetBoundDrawFBO() {
GLint ret = 0;
if (SupportsOffscreenSplit())
fGetIntegerv(LOCAL_GL_DRAW_FRAMEBUFFER_BINDING_EXT, &ret);
else
fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, &ret);
return ret;
}
GLuint GetBoundReadFBO() {
GLint ret = 0;
if (SupportsOffscreenSplit())
fGetIntegerv(LOCAL_GL_READ_FRAMEBUFFER_BINDING_EXT, &ret);
else
fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, &ret);
return ret;
}
void BindDrawFBO(GLuint name) {
if (SupportsOffscreenSplit())
fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER_EXT, name);
else
fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, name);
}
void BindReadFBO(GLuint name) {
if (SupportsOffscreenSplit())
fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER_EXT, name);
else
fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, name);
}
GLuint SwapBoundDrawFBO(GLuint name) {
GLuint prev = GetBoundDrawFBO();
BindDrawFBO(name);
return prev;
}
GLuint SwapBoundReadFBO(GLuint name) {
GLuint prev = GetBoundReadFBO();
BindReadFBO(name);
return prev;
}
void BindOffscreenDrawBuffer() {
BindDrawFBO(mOffscreenDrawFBO);
}
void BindOffscreenReadBuffer() {
BindReadFBO(mOffscreenReadFBO);
}
void BindOffscreenBuffers() {
BindOffscreenDrawBuffer();
BindOffscreenReadBuffer();
}
private:
GLuint mPrevDrawFBOBinding;
GLuint mPrevReadFBOBinding;
bool mOffscreenFBOsDirty;
void BeforeGLDrawCall() {
// Record and rebind if necessary
mPrevDrawFBOBinding = GetBoundDrawFBO();
if (mPrevDrawFBOBinding == 0) {
BindDrawFBO(mOffscreenDrawFBO);
} else if (mPrevDrawFBOBinding != mOffscreenDrawFBO)
return;
// Must be after binding the proper FBO
if (mOffscreenDrawFBO == mOffscreenReadFBO)
return;
// If we're already dirty, no need to set it again
if (mOffscreenFBOsDirty)
return;
mOffscreenFBOsDirty = true;
}
void AfterGLDrawCall() {
if (mPrevDrawFBOBinding == 0) {
BindDrawFBO(0);
}
}
void BeforeGLReadCall() {
// Record and rebind if necessary
mPrevReadFBOBinding = GetBoundReadFBO();
if (mPrevReadFBOBinding == 0) {
BindReadFBO(mOffscreenReadFBO);
} else if (mPrevReadFBOBinding != mOffscreenReadFBO)
return;
// Must be after binding the proper FBO
if (mOffscreenDrawFBO == mOffscreenReadFBO)
return;
// If we're not dirty, there's no need to blit
if (!mOffscreenFBOsDirty)
return;
const bool scissor = fIsEnabled(LOCAL_GL_SCISSOR_TEST);
if (scissor)
fDisable(LOCAL_GL_SCISSOR_TEST);
// flip read/draw for blitting
GLuint prevDraw = SwapBoundDrawFBO(mOffscreenReadFBO);
BindReadFBO(mOffscreenDrawFBO); // We know that Read must already be mOffscreenRead, so no need to write that down
GLint width = mOffscreenActualSize.width;
GLint height = mOffscreenActualSize.height;
raw_fBlitFramebuffer(0, 0, width, height,
0, 0, width, height,
LOCAL_GL_COLOR_BUFFER_BIT,
LOCAL_GL_NEAREST);
BindDrawFBO(prevDraw);
BindReadFBO(mOffscreenReadFBO);
if (scissor)
fEnable(LOCAL_GL_SCISSOR_TEST);
mOffscreenFBOsDirty = false;
}
void AfterGLReadCall() {
if (mPrevReadFBOBinding == 0) {
BindReadFBO(0);
}
}
public:
// Draw call hooks:
void fClear(GLbitfield mask) {
BeforeGLDrawCall();
raw_fClear(mask);
AfterGLDrawCall();
}
void fDrawArrays(GLenum mode, GLint first, GLsizei count) {
BeforeGLDrawCall();
raw_fDrawArrays(mode, first, count);
AfterGLDrawCall();
}
void fDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices) {
BeforeGLDrawCall();
raw_fDrawElements(mode, count, type, indices);
AfterGLDrawCall();
}
// Read call hooks:
void fReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels) {
BeforeGLReadCall();
raw_fReadPixels(x, y, width, height, format, type, pixels);
AfterGLReadCall();
}
void fCopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) {
BeforeGLReadCall();
raw_fCopyTexImage2D(target, level, internalformat,
x, y, width, height, border);
AfterGLReadCall();
}
void fCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) {
BeforeGLReadCall();
raw_fCopyTexSubImage2D(target, level, xoffset, yoffset,
x, y, width, height);
AfterGLReadCall();
}
void ForceDirtyFBOs() {
GLuint draw = SwapBoundReadFBO(mOffscreenDrawFBO);
BeforeGLDrawCall();
// no-op; just pretend we did something
AfterGLDrawCall();
BindDrawFBO(draw);
}
void BlitDirtyFBOs() {
GLuint read = SwapBoundReadFBO(mOffscreenReadFBO);
BeforeGLReadCall();
// no-op; we just want to make sure the Read FBO is updated if it needs to be
AfterGLReadCall();
BindReadFBO(read);
}
// Before reads from offscreen texture
void fFinish() {
BeforeGLReadCall();
raw_fFinish();
AfterGLReadCall();
}
// Draw/Read
void fBlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) {
BeforeGLDrawCall();
BeforeGLReadCall();
raw_fBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
AfterGLReadCall();
AfterGLDrawCall();
}
#if defined(MOZ_X11) && defined(MOZ_EGL_XRENDER_COMPOSITE)
virtual gfxASurface* GetOffscreenPixmapSurface()
{
@ -1013,6 +1231,8 @@ public:
ARB_texture_float,
EXT_unpack_subimage,
OES_standard_derivatives,
EXT_framebuffer_blit,
ANGLE_framebuffer_blit,
Extensions_Max
};
@ -1094,7 +1314,8 @@ protected:
// for offscreen implementations that use FBOs.
bool ResizeOffscreenFBO(const gfxIntSize& aSize);
void DeleteOffscreenFBO();
GLuint mOffscreenFBO;
GLuint mOffscreenDrawFBO;
GLuint mOffscreenReadFBO;
GLuint mOffscreenDepthRB;
GLuint mOffscreenStencilRB;
@ -1454,7 +1675,7 @@ public:
AFTER_GL_CALL;
}
void fClear(GLbitfield mask) {
void raw_fClear(GLbitfield mask) {
BEFORE_GL_CALL;
mSymbols.fClear(mask);
AFTER_GL_CALL;
@ -1514,13 +1735,13 @@ public:
AFTER_GL_CALL;
}
void fDrawArrays(GLenum mode, GLint first, GLsizei count) {
void raw_fDrawArrays(GLenum mode, GLint first, GLsizei count) {
BEFORE_GL_CALL;
mSymbols.fDrawArrays(mode, first, count);
AFTER_GL_CALL;
}
void fDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices) {
void raw_fDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices) {
BEFORE_GL_CALL;
mSymbols.fDrawElements(mode, count, type, indices);
AFTER_GL_CALL;
@ -1538,7 +1759,7 @@ public:
AFTER_GL_CALL;
}
void fFinish() {
void raw_fFinish() {
BEFORE_GL_CALL;
mSymbols.fFinish();
AFTER_GL_CALL;
@ -1756,7 +1977,7 @@ public:
AFTER_GL_CALL;
}
void fReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels) {
void raw_fReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels) {
BEFORE_GL_CALL;
mSymbols.fReadPixels(x, y, width, height, format, type, pixels);
AFTER_GL_CALL;
@ -2002,7 +2223,7 @@ public:
AFTER_GL_CALL;
}
void fCopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) {
void raw_fCopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) {
BEFORE_GL_CALL;
mSymbols.fCopyTexImage2D(target, level, internalformat,
x, FixYValue(y, height),
@ -2010,7 +2231,7 @@ public:
AFTER_GL_CALL;
}
void fCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) {
void raw_fCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) {
BEFORE_GL_CALL;
mSymbols.fCopyTexSubImage2D(target, level, xoffset, yoffset,
x, FixYValue(y, height),
@ -2092,6 +2313,12 @@ public:
return retval;
}
void raw_fBlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) {
BEFORE_GL_CALL;
mSymbols.fBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
AFTER_GL_CALL;
}
realGLboolean fIsRenderbuffer (GLuint renderbuffer) {
BEFORE_GL_CALL;
realGLboolean retval = mSymbols.fIsRenderbuffer(renderbuffer);

View File

@ -307,6 +307,9 @@ struct GLContextSymbols
typedef void (GLAPIENTRY * PFNGLRENDERBUFFERSTORAGE) (GLenum target, GLenum internalFormat, GLsizei width, GLsizei height);
PFNGLRENDERBUFFERSTORAGE fRenderbufferStorage;
typedef void (GLAPIENTRY * PFNGLBLITFRAMEBUFFER) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
PFNGLBLITFRAMEBUFFER fBlitFramebuffer;
/* These are different between GLES2 and desktop GL; we hide those differences, use the GL
* names, but the most limited data type.