diff --git a/content/canvas/src/WebGLContext.cpp b/content/canvas/src/WebGLContext.cpp index dc0803fb346..421d79e2449 100644 --- a/content/canvas/src/WebGLContext.cpp +++ b/content/canvas/src/WebGLContext.cpp @@ -170,7 +170,9 @@ WebGLContext::SetDimensions(PRInt32 width, PRInt32 height) format.depth = 16; format.minDepth = 1; - gl = gl::GLContextProvider::CreatePBuffer(gfxIntSize(width, height), format); + gl = gl::GLContextProvider::CreatePBuffer(gfxIntSize(width, height), + gl::GLContextProvider::GetGlobalContext(), + format); #ifdef USE_GLES2 // On native GLES2, no need to validate, the compiler will do it @@ -184,7 +186,9 @@ WebGLContext::SetDimensions(PRInt32 width, PRInt32 height) #endif if (!InitAndValidateGL()) { - gl = gl::GLContextProviderOSMesa::CreatePBuffer(gfxIntSize(width, height), format); + gl = gl::GLContextProviderOSMesa::CreatePBuffer(gfxIntSize(width, height), + nsnull, + format); if (!InitAndValidateGL()) { LogMessage("WebGL: Can't get a usable OpenGL context."); return NS_ERROR_FAILURE; diff --git a/gfx/layers/opengl/LayerManagerOGL.cpp b/gfx/layers/opengl/LayerManagerOGL.cpp index b6e6caa2699..8dec0f70dfa 100644 --- a/gfx/layers/opengl/LayerManagerOGL.cpp +++ b/gfx/layers/opengl/LayerManagerOGL.cpp @@ -76,15 +76,40 @@ LayerManagerOGL::LayerManagerOGL(nsIWidget *aWidget) LayerManagerOGL::~LayerManagerOGL() { - if (mGLContext) - mGLContext->MakeCurrent(); + mRoot = nsnull; + CleanupResources(); +} - mRoot = NULL; +void +LayerManagerOGL::CleanupResources() +{ + if (!mGLContext) + return; + + mGLContext->MakeCurrent(); for (unsigned int i = 0; i < mPrograms.Length(); ++i) delete mPrograms[i]; - mPrograms.Clear(); + + mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0); + + if (mBackBufferFBO) { + mGLContext->fDeleteFramebuffers(1, &mBackBufferFBO); + mBackBufferFBO = 0; + } + + if (mBackBufferTexture) { + mGLContext->fDeleteTextures(1, &mBackBufferTexture); + mBackBufferTexture = 0; + } + + if (mQuadVBO) { + mGLContext->fDeleteBuffers(1, &mQuadVBO); + mQuadVBO = 0; + } + + mGLContext = nsnull; } PRBool @@ -93,6 +118,8 @@ LayerManagerOGL::Initialize(GLContext *aExistingContext) if (aExistingContext) { mGLContext = aExistingContext; } else { + if (mGLContext) + CleanupResources(); mGLContext = gl::GLContextProvider::CreateForWindow(mWidget); if (!mGLContext) { diff --git a/gfx/layers/opengl/LayerManagerOGL.h b/gfx/layers/opengl/LayerManagerOGL.h index 648cf8c9121..bfb76b7992f 100644 --- a/gfx/layers/opengl/LayerManagerOGL.h +++ b/gfx/layers/opengl/LayerManagerOGL.h @@ -80,7 +80,9 @@ class THEBES_API LayerManagerOGL : public LayerManager { public: LayerManagerOGL(nsIWidget *aWidget); virtual ~LayerManagerOGL(); - + + void CleanupResources(); + /** * Initializes the layer manager, this is when the layer manager will * actually access the device and attempt to create the swap chain used diff --git a/gfx/layers/opengl/LayerManagerOGLProgram.h b/gfx/layers/opengl/LayerManagerOGLProgram.h index c2454eb0340..69aa4c3d7fa 100644 --- a/gfx/layers/opengl/LayerManagerOGLProgram.h +++ b/gfx/layers/opengl/LayerManagerOGLProgram.h @@ -120,7 +120,10 @@ public: : mGL(aGL), mProgram(0) { } - virtual ~LayerManagerOGLProgram() { } + virtual ~LayerManagerOGLProgram() { + mGL->MakeCurrent(); + mGL->fDeleteProgram(mProgram); + } void Activate() { NS_ASSERTION(mProgram != 0, "Attempting to activate a program that's not in use!"); @@ -199,11 +202,9 @@ public: } protected: - GLContext *mGL; + nsRefPtr mGL; GLuint mProgram; - GLuint mFragmentShader; - GLuint mVertexShader; nsTArray mUniformValues; @@ -254,15 +255,15 @@ protected: bool CreateProgram(const char *aVertexShaderString, const char *aFragmentShaderString) { - mVertexShader = CreateShader(LOCAL_GL_VERTEX_SHADER, aVertexShaderString); - mFragmentShader = CreateShader(LOCAL_GL_FRAGMENT_SHADER, aFragmentShaderString); + GLuint vertexShader = CreateShader(LOCAL_GL_VERTEX_SHADER, aVertexShaderString); + GLuint fragmentShader = CreateShader(LOCAL_GL_FRAGMENT_SHADER, aFragmentShaderString); - if (!mVertexShader || !mFragmentShader) + if (!vertexShader || !fragmentShader) return false; mProgram = mGL->fCreateProgram(); - mGL->fAttachShader(mProgram, mVertexShader); - mGL->fAttachShader(mProgram, mFragmentShader); + mGL->fAttachShader(mProgram, vertexShader); + mGL->fAttachShader(mProgram, fragmentShader); // bind common attribs to consistent indices mGL->fBindAttribLocation(mProgram, VertexAttrib, "aVertexCoord"); @@ -296,10 +297,13 @@ protected: fprintf (stderr, "=== Log:\n%s\n", nsPromiseFlatCString(log).get()); fprintf (stderr, "============\n"); + // We can mark the shaders for deletion; they're attached to the program + // and will remain attached. + mGL->fDeleteShader(vertexShader); + mGL->fDeleteShader(fragmentShader); + if (!success) { mGL->fDeleteProgram(mProgram); - mGL->fDeleteShader(mVertexShader); - mGL->fDeleteShader(mFragmentShader); mProgram = 0; diff --git a/gfx/thebes/GLContext.cpp b/gfx/thebes/GLContext.cpp index 65bb648d495..7481dfc65e3 100644 --- a/gfx/thebes/GLContext.cpp +++ b/gfx/thebes/GLContext.cpp @@ -111,6 +111,7 @@ LibrarySymbolLoader::LoadSymbols(PRLibrary *lib, const char *prefix) { char sbuf[MAX_SYMBOL_LENGTH * 2]; + int failCount = 0; SymLoadStruct *ss = firstStruct; while (ss->symPointer) { @@ -136,13 +137,13 @@ LibrarySymbolLoader::LoadSymbols(PRLibrary *lib, if (*ss->symPointer == 0) { fprintf (stderr, "Can't find symbol '%s'\n", ss->symNames[0]); - return PR_FALSE; + failCount++; } ss++; } - return PR_TRUE; + return failCount == 0 ? PR_TRUE : PR_FALSE; } /* @@ -179,13 +180,7 @@ GLContext::InitWithPrefix(const char *prefix, PRBool trygl) #endif { (PRFuncPtr*) &fClearStencil, { "ClearStencil", NULL } }, { (PRFuncPtr*) &fColorMask, { "ColorMask", NULL } }, - { (PRFuncPtr*) &fCreateProgram, { "CreateProgram", "CreateProgramARB", NULL } }, - { (PRFuncPtr*) &fCreateShader, { "CreateShader", "CreateShaderARB", NULL } }, { (PRFuncPtr*) &fCullFace, { "CullFace", NULL } }, - { (PRFuncPtr*) &fDeleteBuffers, { "DeleteBuffers", "DeleteBuffersARB", NULL } }, - { (PRFuncPtr*) &fDeleteTextures, { "DeleteTextures", "DeleteTexturesARB", NULL } }, - { (PRFuncPtr*) &fDeleteProgram, { "DeleteProgram", "DeleteProgramARB", NULL } }, - { (PRFuncPtr*) &fDeleteShader, { "DeleteShader", "DeleteShaderARB", NULL } }, { (PRFuncPtr*) &fDetachShader, { "DetachShader", "DetachShaderARB", NULL } }, { (PRFuncPtr*) &fDepthFunc, { "DepthFunc", NULL } }, { (PRFuncPtr*) &fDepthMask, { "DepthMask", NULL } }, @@ -217,8 +212,6 @@ GLContext::InitWithPrefix(const char *prefix, PRBool trygl) { (PRFuncPtr*) &fGetFloatv, { "GetFloatv", NULL } }, { (PRFuncPtr*) &fGetBooleanv, { "GetBooleanv", NULL } }, { (PRFuncPtr*) &fGetBufferParameteriv, { "GetBufferParameteriv", "GetBufferParameterivARB", NULL } }, - { (PRFuncPtr*) &fGenBuffers, { "GenBuffers", "GenBuffersARB", NULL } }, - { (PRFuncPtr*) &fGenTextures, { "GenTextures", NULL } }, { (PRFuncPtr*) &fGetError, { "GetError", NULL } }, { (PRFuncPtr*) &fGetProgramiv, { "GetProgramiv", "GetProgramivARB", NULL } }, { (PRFuncPtr*) &fGetProgramInfoLog, { "GetProgramInfoLog", "GetProgramInfoLogARB", NULL } }, @@ -307,22 +300,28 @@ GLContext::InitWithPrefix(const char *prefix, PRBool trygl) { (PRFuncPtr*) &fBindFramebuffer, { "BindFramebuffer", "BindFramebufferEXT", NULL } }, { (PRFuncPtr*) &fBindRenderbuffer, { "BindRenderbuffer", "BindRenderbufferEXT", NULL } }, { (PRFuncPtr*) &fCheckFramebufferStatus, { "CheckFramebufferStatus", "CheckFramebufferStatusEXT", NULL } }, - { (PRFuncPtr*) &fDeleteFramebuffers, { "DeleteFramebuffers", "DeleteFramebuffersEXT", NULL } }, - { (PRFuncPtr*) &fDeleteRenderbuffers, { "DeleteRenderbuffers", "DeleteRenderbuffersEXT", NULL } }, { (PRFuncPtr*) &fFramebufferRenderbuffer, { "FramebufferRenderbuffer", "FramebufferRenderbufferEXT", NULL } }, { (PRFuncPtr*) &fFramebufferTexture2D, { "FramebufferTexture2D", "FramebufferTexture2DEXT", NULL } }, { (PRFuncPtr*) &fGenerateMipmap, { "GenerateMipmap", "GenerateMipmapEXT", NULL } }, - { (PRFuncPtr*) &fGenFramebuffers, { "GenFramebuffers", "GenFramebuffersEXT", NULL } }, - { (PRFuncPtr*) &fGenRenderbuffers, { "GenRenderbuffers", "GenRenderbuffersEXT", NULL } }, { (PRFuncPtr*) &fGetFramebufferAttachmentParameteriv, { "GetFramebufferAttachmentParameteriv", "GetFramebufferAttachmentParameterivEXT", NULL } }, { (PRFuncPtr*) &fGetRenderbufferParameteriv, { "GetRenderbufferParameteriv", "GetRenderbufferParameterivEXT", NULL } }, { (PRFuncPtr*) &fIsFramebuffer, { "IsFramebuffer", "IsFramebufferEXT", NULL } }, { (PRFuncPtr*) &fIsRenderbuffer, { "IsRenderbuffer", "IsRenderbufferEXT", NULL } }, { (PRFuncPtr*) &fRenderbufferStorage, { "RenderbufferStorage", "RenderbufferStorageEXT", NULL } }, -#if 0 - { (PRFuncPtr*) &fMapBuffer, { "MapBuffer", NULL } }, - { (PRFuncPtr*) &fUnmapBuffer, { "UnmapBuffer", NULL } }, -#endif + + { (PRFuncPtr*) &priv_fGenBuffers, { "GenBuffers", "GenBuffersARB", NULL } }, + { (PRFuncPtr*) &priv_fGenTextures, { "GenTextures", NULL } }, + { (PRFuncPtr*) &priv_fCreateProgram, { "CreateProgram", "CreateProgramARB", NULL } }, + { (PRFuncPtr*) &priv_fCreateShader, { "CreateShader", "CreateShaderARB", NULL } }, + { (PRFuncPtr*) &priv_fGenFramebuffers, { "GenFramebuffers", "GenFramebuffersEXT", NULL } }, + { (PRFuncPtr*) &priv_fGenRenderbuffers, { "GenRenderbuffers", "GenRenderbuffersEXT", NULL } }, + + { (PRFuncPtr*) &priv_fDeleteBuffers, { "DeleteBuffers", "DeleteBuffersARB", NULL } }, + { (PRFuncPtr*) &priv_fDeleteTextures, { "DeleteTextures", "DeleteTexturesARB", NULL } }, + { (PRFuncPtr*) &priv_fDeleteProgram, { "DeleteProgram", "DeleteProgramARB", NULL } }, + { (PRFuncPtr*) &priv_fDeleteShader, { "DeleteShader", "DeleteShaderARB", NULL } }, + { (PRFuncPtr*) &priv_fDeleteFramebuffers, { "DeleteFramebuffers", "DeleteFramebuffersEXT", NULL } }, + { (PRFuncPtr*) &priv_fDeleteRenderbuffers, { "DeleteRenderbuffers", "DeleteRenderbuffersEXT", NULL } }, { NULL, { NULL } }, @@ -472,5 +471,361 @@ BasicTextureImage::EndUpdate() return PR_TRUE; // mTexture is bound } +PRBool +GLContext::ResizeOffscreenFBO(const gfxIntSize& aSize) +{ + MakeCurrent(); + + bool alpha = mCreationFormat.alpha > 0; + int depth = mCreationFormat.depth; + int stencil = mCreationFormat.stencil; + +#ifdef USE_GLES2 + const bool isMobile = true; +#else + const bool isMobile = false; +#endif + + bool firstTime = (mOffscreenFBO == 0); + + GLuint curBoundTexture = 0; + GLuint curBoundRenderbuffer = 0; + GLuint curBoundFramebuffer = 0; + + GLint viewport[4]; + + // save a few things for later restoring + fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, (GLint*) &curBoundTexture); + fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, (GLint*) &curBoundFramebuffer); + fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING, (GLint*) &curBoundRenderbuffer); + fGetIntegerv(LOCAL_GL_VIEWPORT, viewport); + + // If this is the first time we're going through this, we need + // to create the objects we'll use. Otherwise, just bind them. + if (firstTime) { + fGenTextures(1, &mOffscreenTexture); + fBindTexture(LOCAL_GL_TEXTURE_2D, mOffscreenTexture); + fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR); + fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR); + + fGenFramebuffers(1, &mOffscreenFBO); + fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mOffscreenFBO); + + if (depth) { + fGenRenderbuffers(1, &mOffscreenDepthRB); + } + + if (stencil) { + fGenRenderbuffers(1, &mOffscreenStencilRB); + } + } else { + fBindTexture(LOCAL_GL_TEXTURE_2D, mOffscreenTexture); + fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mOffscreenFBO); + } + + // resize the FBO components + if (alpha) { + fTexImage2D(LOCAL_GL_TEXTURE_2D, + 0, + LOCAL_GL_RGBA, + aSize.width, aSize.height, + 0, + LOCAL_GL_RGBA, + LOCAL_GL_UNSIGNED_BYTE, + NULL); + } else { + fTexImage2D(LOCAL_GL_TEXTURE_2D, + 0, + LOCAL_GL_RGB, + aSize.width, aSize.height, + 0, + LOCAL_GL_RGB, + isMobile ? LOCAL_GL_UNSIGNED_SHORT_5_6_5 + : LOCAL_GL_UNSIGNED_BYTE, + NULL); + } + + + if (depth) { + fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mOffscreenDepthRB); + fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, + LOCAL_GL_DEPTH_COMPONENT16, + aSize.width, aSize.height); + } + + if (stencil) { + fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mOffscreenStencilRB); + fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, + LOCAL_GL_STENCIL_INDEX8, + aSize.width, aSize.height); + } + + // Now assemble the FBO, if we're creating one + // for the first time. + if (firstTime) { + fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, + LOCAL_GL_COLOR_ATTACHMENT0, + LOCAL_GL_TEXTURE_2D, + mOffscreenTexture, + 0); + if (depth) { + fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, + LOCAL_GL_DEPTH_ATTACHMENT, + LOCAL_GL_RENDERBUFFER, + mOffscreenDepthRB); + } + + if (stencil) { + fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, + LOCAL_GL_STENCIL_ATTACHMENT, + LOCAL_GL_RENDERBUFFER, + mOffscreenStencilRB); + } + } + + // We should be all resized. Check for framebuffer completeness. + GLenum status = fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER); + if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) { + NS_WARNING("Error resizing offscreen framebuffer -- framebuffer not complete"); + return PR_FALSE; + } + + mOffscreenSize = aSize; + mOffscreenActualSize = aSize; + + if (firstTime) { + UpdateActualFormat(); + } + + // We're good, and the framebuffer is already attached, so let's + // clear out our new framebuffer; otherwise we'll end up displaying + // random memory. We saved all of these things earlier so that we + // can restore them. + fViewport(0, 0, aSize.width, aSize.height); + + // Ok, now restore the GL state back to what it was before the resize took place. + fBindTexture(LOCAL_GL_TEXTURE_2D, curBoundTexture); + fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, curBoundRenderbuffer); + fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, curBoundFramebuffer); + + // -don't- restore the viewport the first time through this, since + // the previous one isn't valid. + if (!firstTime) + fViewport(viewport[0], viewport[1], viewport[2], viewport[3]); + + ClearSafely(); + + return PR_TRUE; +} + +void +GLContext::DeleteOffscreenFBO() +{ + fDeleteFramebuffers(1, &mOffscreenFBO); + fDeleteTextures(1, &mOffscreenTexture); + fDeleteRenderbuffers(1, &mOffscreenDepthRB); + fDeleteRenderbuffers(1, &mOffscreenStencilRB); + + mOffscreenFBO = 0; + mOffscreenTexture = 0; + mOffscreenDepthRB = 0; + mOffscreenStencilRB = 0; +} + +void +GLContext::ClearSafely() +{ + GLfloat clearColor[4]; + GLfloat clearDepth; + GLint clearStencil; + + fGetFloatv(LOCAL_GL_COLOR_CLEAR_VALUE, clearColor); + fGetFloatv(LOCAL_GL_DEPTH_CLEAR_VALUE, &clearDepth); + fGetIntegerv(LOCAL_GL_STENCIL_CLEAR_VALUE, &clearStencil); + + fClearColor(0.0f, 0.0f, 0.0f, 0.0f); + fClearStencil(0); +#ifdef USE_GLES2 + fClearDepthf(1.0f); +#else + fClearDepth(1.0); +#endif + + fClear(LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT | LOCAL_GL_STENCIL_BUFFER_BIT); + + fClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); + fClearStencil(clearStencil); +#ifdef USE_GLES2 + fClearDepthf(clearDepth); +#else + fClearDepth(clearDepth); +#endif +} + +void +GLContext::UpdateActualFormat() +{ + // TODO +} + +#ifdef DEBUG + +void +GLContext::CreatedProgram(GLContext *aOrigin, GLuint aName) +{ + mTrackedPrograms.AppendElement(NamedResource(aOrigin, aName)); +} + +void +GLContext::CreatedShader(GLContext *aOrigin, GLuint aName) +{ + mTrackedShaders.AppendElement(NamedResource(aOrigin, aName)); +} + +void +GLContext::CreatedBuffers(GLContext *aOrigin, GLsizei aCount, GLuint *aNames) +{ + for (GLsizei i = 0; i < aCount; ++i) { + mTrackedBuffers.AppendElement(NamedResource(aOrigin, aNames[i])); + } +} + +void +GLContext::CreatedTextures(GLContext *aOrigin, GLsizei aCount, GLuint *aNames) +{ + for (GLsizei i = 0; i < aCount; ++i) { + mTrackedTextures.AppendElement(NamedResource(aOrigin, aNames[i])); + } +} + +void +GLContext::CreatedFramebuffers(GLContext *aOrigin, GLsizei aCount, GLuint *aNames) +{ + for (GLsizei i = 0; i < aCount; ++i) { + mTrackedFramebuffers.AppendElement(NamedResource(aOrigin, aNames[i])); + } +} + +void +GLContext::CreatedRenderbuffers(GLContext *aOrigin, GLsizei aCount, GLuint *aNames) +{ + for (GLsizei i = 0; i < aCount; ++i) { + mTrackedRenderbuffers.AppendElement(NamedResource(aOrigin, aNames[i])); + } +} + +static void +RemoveNamesFromArray(GLContext *aOrigin, GLsizei aCount, GLuint *aNames, nsTArray& aArray) +{ + for (GLsizei j = 0; j < aCount; ++j) { + GLuint name = aNames[j]; + PRBool found = PR_FALSE; + for (PRUint32 i = 0; i < aArray.Length(); ++i) { + if (aArray[i].name == name) { + aArray.RemoveElementAt(i); + found = PR_TRUE; + break; + } + } + if (!found) + printf_stderr("GL Context %p deleting resource %d, which doesn't exist!", aOrigin, name); + } +} + +void +GLContext::DeletedProgram(GLContext *aOrigin, GLuint aName) +{ + RemoveNamesFromArray(aOrigin, 1, &aName, mTrackedPrograms); +} + +void +GLContext::DeletedShader(GLContext *aOrigin, GLuint aName) +{ + RemoveNamesFromArray(aOrigin, 1, &aName, mTrackedShaders); +} + +void +GLContext::DeletedBuffers(GLContext *aOrigin, GLsizei aCount, GLuint *aNames) +{ + RemoveNamesFromArray(aOrigin, aCount, aNames, mTrackedBuffers); +} + +void +GLContext::DeletedTextures(GLContext *aOrigin, GLsizei aCount, GLuint *aNames) +{ + RemoveNamesFromArray(aOrigin, aCount, aNames, mTrackedTextures); +} + +void +GLContext::DeletedFramebuffers(GLContext *aOrigin, GLsizei aCount, GLuint *aNames) +{ + RemoveNamesFromArray(aOrigin, aCount, aNames, mTrackedFramebuffers); +} + +void +GLContext::DeletedRenderbuffers(GLContext *aOrigin, GLsizei aCount, GLuint *aNames) +{ + RemoveNamesFromArray(aOrigin, aCount, aNames, mTrackedRenderbuffers); +} + +static void +MarkContextDestroyedInArray(GLContext *aContext, nsTArray& aArray) +{ + for (PRUint32 i = 0; i < aArray.Length(); ++i) { + if (aArray[i].origin == aContext) + aArray[i].originDeleted = PR_TRUE; + } +} + +void +GLContext::SharedContextDestroyed(GLContext *aChild) +{ + MarkContextDestroyedInArray(aChild, mTrackedPrograms); + MarkContextDestroyedInArray(aChild, mTrackedShaders); + MarkContextDestroyedInArray(aChild, mTrackedTextures); + MarkContextDestroyedInArray(aChild, mTrackedFramebuffers); + MarkContextDestroyedInArray(aChild, mTrackedRenderbuffers); + MarkContextDestroyedInArray(aChild, mTrackedBuffers); +} + +static void +ReportArrayContents(const nsTArray& aArray) +{ + nsTArray copy(aArray); + copy.Sort(); + + GLContext *lastContext = NULL; + for (PRUint32 i = 0; i < copy.Length(); ++i) { + if (lastContext != copy[i].origin) { + if (lastContext) + printf_stderr("\n"); + printf_stderr(" [%p - %s] ", copy[i].origin, copy[i].originDeleted ? "deleted" : "live"); + lastContext = copy[i].origin; + } + printf_stderr("%d ", copy[i].name); + } + printf_stderr("\n"); +} + +void +GLContext::ReportOutstandingNames() +{ + printf_stderr("== GLContext %p ==\n", this); + printf_stderr("Outstanding Textures:\n"); + ReportArrayContents(mTrackedTextures); + printf_stderr("Outstanding Buffers:\n"); + ReportArrayContents(mTrackedBuffers); + printf_stderr("Outstanding Programs:\n"); + ReportArrayContents(mTrackedPrograms); + printf_stderr("Outstanding Shaders:\n"); + ReportArrayContents(mTrackedShaders); + printf_stderr("Outstanding Framebuffers:\n"); + ReportArrayContents(mTrackedFramebuffers); + printf_stderr("Outstanding Renderbuffers:\n"); + ReportArrayContents(mTrackedRenderbuffers); +} + +#endif /* DEBUG */ + } /* namespace gl */ } /* namespace mozilla */ diff --git a/gfx/thebes/GLContext.h b/gfx/thebes/GLContext.h index efd80e48186..0a24d0a3a18 100644 --- a/gfx/thebes/GLContext.h +++ b/gfx/thebes/GLContext.h @@ -57,6 +57,7 @@ #include "nsDataHashtable.h" #include "nsHashKeys.h" #include "nsRegion.h" +#include "nsAutoPtr.h" #ifndef GLAPIENTRY #ifdef XP_WIN @@ -249,19 +250,110 @@ protected: nsIntRect mUpdateRect; }; +struct THEBES_API ContextFormat { + static const ContextFormat BasicRGBA32Format; + + enum StandardContextFormat { + Empty, + BasicRGBA32, + StrictBasicRGBA32, + BasicRGB24, + StrictBasicRGB24, + BasicRGB16_565, + StrictBasicRGB16_565 + }; + + ContextFormat() { + memset(this, 0, sizeof(ContextFormat)); + } + + ContextFormat(const StandardContextFormat cf) { + memset(this, 0, sizeof(ContextFormat)); + + switch (cf) { + case BasicRGBA32: + red = green = blue = alpha = 8; + minRed = minGreen = minBlue = minAlpha = 1; + break; + + case StrictBasicRGBA32: + red = green = blue = alpha = 8; + minRed = minGreen = minBlue = minAlpha = 8; + break; + + case BasicRGB24: + red = green = blue = 8; + minRed = minGreen = minBlue = 1; + break; + + case StrictBasicRGB24: + red = green = blue = 8; + minRed = minGreen = minBlue = 8; + break; + + case StrictBasicRGB16_565: + red = minRed = 5; + green = minGreen = 6; + blue = minBlue = 5; + break; + + default: + break; + } + } + + int depth, minDepth; + int stencil, minStencil; + int red, minRed; + int green, minGreen; + int blue, minBlue; + int alpha, minAlpha; + + int colorBits() const { return red + green + blue; } +}; + class GLContext : public LibrarySymbolLoader { THEBES_INLINE_DECL_THREADSAFE_REFCOUNTING(GLContext) public: - GLContext() - : mInitialized(PR_FALSE) + GLContext(const ContextFormat& aFormat, + PRBool aIsOffscreen = PR_FALSE, + GLContext *aSharedContext = nsnull) + : mInitialized(PR_FALSE), + mCreationFormat(aFormat), + mIsOffscreen(aIsOffscreen), + mSharedContext(aSharedContext), + mOffscreenTexture(0), + mOffscreenFBO(0), + mOffscreenDepthRB(0), + mOffscreenStencilRB(0) { mUserData.Init(); } - virtual ~GLContext() { } + virtual ~GLContext() { +#ifdef DEBUG + if (mSharedContext) { + GLContext *tip = mSharedContext; + while (tip->mSharedContext) + tip = tip->mSharedContext; + tip->SharedContextDestroyed(this); + tip->ReportOutstandingNames(); + } +#endif + } + enum GLContextType { + ContextTypeUnknown, + ContextTypeWGL, + ContextTypeCGL, + ContextTypeGLX, + ContextTypeEGL, + ContextTypeOSMesa + }; + + virtual GLContextType GetContextType() { return ContextTypeUnknown; } virtual PRBool MakeCurrent() = 0; virtual PRBool SetupLookupFunction() = 0; @@ -279,23 +371,22 @@ public: enum NativeDataType { NativeGLContext, - NativeCGLContext, - NativePBuffer, NativeImageSurface, + NativeThebesSurface, NativeDataTypeMax }; virtual void *GetNativeData(NativeDataType aType) { return NULL; } + GLContext *GetSharedContext() { return mSharedContext; } - /* If this is a PBuffer context, resize the pbufer to the given dimensions, - * keping the same format and attributes. If the resize succeeds, return - * PR_TRUE. Otherwise, or if this is not a pbuffer, return PR_FALSE. - * - * On a successful resize, the previous contents of the pbuffer are cleared, - * and the new contents are undefined. + const ContextFormat& CreationFormat() { return mCreationFormat; } + const ContextFormat& ActualFormat() { return mActualFormat; } + + /** + * If this context is double-buffered, returns TRUE. */ - virtual PRBool Resize(const gfxIntSize& aNewSize) { return PR_FALSE; } - + virtual PRBool IsDoubleBuffered() { return PR_FALSE; } + /** * If this context wraps a double-buffered target, swap the back * and front buffers. It should be assumed that after a swap, the @@ -326,6 +417,99 @@ public: fDeleteTextures(1, &tex); } + /* + * Offscreen support API + */ + + /* + * Bind aOffscreen's color buffer as a texture to the TEXTURE_2D + * target. Returns TRUE on success, otherwise FALSE. If + * aOffscreen is not an offscreen context, returns FALSE. If + * BindOffscreenNeedsTexture() returns TRUE, then you should have + * a 2D texture name bound whose image will be replaced by the + * contents of the offscreen context. If it returns FALSE, + * the current 2D texture binding will be replaced. + * + * After a successul call to BindTex2DOffscreen, UnbindTex2DOffscreen + * *must* be called once rendering is complete. + * + * The same texture unit must be active for Bind/Unbind of a given + * context. + */ + virtual PRBool BindOffscreenNeedsTexture(GLContext *aOffscreen) { + return aOffscreen->mOffscreenTexture == 0; + } + + virtual PRBool BindTex2DOffscreen(GLContext *aOffscreen) { + if (aOffscreen->GetContextType() != GetContextType()) { + return PR_FALSE; + } + + if (!aOffscreen->mOffscreenFBO) { + return PR_FALSE; + } + + if (!aOffscreen->mSharedContext || + aOffscreen->mSharedContext != mSharedContext) + { + return PR_FALSE; + } + + fBindTexture(LOCAL_GL_TEXTURE_2D, aOffscreen->mOffscreenTexture); + + return PR_TRUE; + } + + virtual void UnbindTex2DOffscreen(GLContext *aOffscreen) { } + + PRBool IsOffscreen() { + return mIsOffscreen; + } + + /* + * All the methods below are only valid if IsOffscreen() returns + * true. + */ + + /* + * Resize the current offscreen buffer. Returns true on success. + * If it returns false, the context should be treated as unusable + * and should be recreated. After the resize, the viewport is not + * changed; glViewport should be called as appropriate. + */ + virtual PRBool ResizeOffscreen(const gfxIntSize& aNewSize) { + if (mOffscreenFBO) + return ResizeOffscreenFBO(aNewSize); + return PR_FALSE; + } + + /* + * Return size of this offscreen context. + */ + gfxIntSize OffscreenSize() { + return mOffscreenSize; + } + + /* + * In some cases, we have to allocate a bigger offscreen buffer + * than what's requested. This is the bigger size. + */ + gfxIntSize OffscreenActualSize() { + return mOffscreenActualSize; + } + + /* + * If this context is FBO-backed, return the FBO or the color + * buffer texture. If the context is not FBO-backed, 0 is + * returned (which is also a valid FBO binding). + */ + GLuint GetOffscreenFBO() { + return mOffscreenFBO; + } + GLuint GetOffscreenTexture() { + return mOffscreenTexture; + } + /** * Return a valid, allocated TextureImage of |aSize| with * |aContentType|. The TextureImage's texture is configured to @@ -346,8 +530,31 @@ public: PRBool aUseNearestFilter=PR_FALSE); protected: + PRPackedBool mInitialized; + ContextFormat mCreationFormat; + PRPackedBool mIsOffscreen; + nsRefPtr mSharedContext; + + void UpdateActualFormat(); + ContextFormat mActualFormat; + + gfxIntSize mOffscreenSize; + gfxIntSize mOffscreenActualSize; + GLuint mOffscreenTexture; + + // helper to create/resize an offscreen FBO, + // for offscreen implementations that use FBOs. + PRBool ResizeOffscreenFBO(const gfxIntSize& aSize); + void DeleteOffscreenFBO(); + GLuint mOffscreenFBO; + GLuint mOffscreenDepthRB; + GLuint mOffscreenStencilRB; + + // Clear to transparent black, with 0 depth and stencil, + // while preserving current ClearColor etc. values. + // Useful for resizing offscreen buffers. + void ClearSafely(); - PRBool mInitialized; nsDataHashtable mUserData; PRBool InitWithPrefix(const char *prefix, PRBool trygl); @@ -408,20 +615,8 @@ public: PFNGLCLEARSTENCILPROC fClearStencil; typedef void (GLAPIENTRY * PFNGLCOLORMASKPROC) (realGLboolean red, realGLboolean green, realGLboolean blue, realGLboolean alpha); PFNGLCOLORMASKPROC fColorMask; - typedef GLuint (GLAPIENTRY * PFNGLCREATEPROGRAMPROC) (void); - PFNGLCREATEPROGRAMPROC fCreateProgram; - typedef GLuint (GLAPIENTRY * PFNGLCREATESHADERPROC) (GLenum type); - PFNGLCREATESHADERPROC fCreateShader; typedef void (GLAPIENTRY * PFNGLCULLFACEPROC) (GLenum mode); PFNGLCULLFACEPROC fCullFace; - typedef void (GLAPIENTRY * PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint* buffers); - PFNGLDELETEBUFFERSPROC fDeleteBuffers; - typedef void (GLAPIENTRY * PFNGLDELETETEXTURESPROC) (GLsizei n, const GLuint* textures); - PFNGLDELETETEXTURESPROC fDeleteTextures; - typedef void (GLAPIENTRY * PFNGLDELETEPROGRAMPROC) (GLuint program); - PFNGLDELETEPROGRAMPROC fDeleteProgram; - typedef void (GLAPIENTRY * PFNGLDELETESHADERPROC) (GLuint shader); - PFNGLDELETESHADERPROC fDeleteShader; typedef void (GLAPIENTRY * PFNGLDETACHSHADERPROC) (GLuint program, GLuint shader); PFNGLDETACHSHADERPROC fDetachShader; typedef void (GLAPIENTRY * PFNGLDEPTHFUNCPROC) (GLenum); @@ -473,10 +668,6 @@ public: PFNGLGETBOOLEANBPROC fGetBooleanv; typedef void (GLAPIENTRY * PFNGLGETBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint* params); PFNGLGETBUFFERPARAMETERIVPROC fGetBufferParameteriv; - typedef void (GLAPIENTRY * PFNGLGENBUFFERSPROC) (GLsizei n, GLuint* buffers); - PFNGLGENBUFFERSPROC fGenBuffers; - typedef void (GLAPIENTRY * PFNGLGENTEXTURESPROC) (GLsizei n, GLuint *textures); - PFNGLGENTEXTURESPROC fGenTextures; typedef void (GLAPIENTRY * PFNGLGENERATEMIPMAPPROC) (GLenum target); PFNGLGENERATEMIPMAPPROC fGenerateMipmap; typedef GLenum (GLAPIENTRY * PFNGLGETERRORPROC) (void); @@ -523,12 +714,6 @@ public: PFNGLLINEWIDTHPROC fLineWidth; typedef void (GLAPIENTRY * PFNGLLINKPROGRAMPROC) (GLuint program); PFNGLLINKPROGRAMPROC fLinkProgram; -#if 0 - typedef void * (GLAPIENTRY * PFNGLMAPBUFFERPROC) (GLenum target, GLenum access); - PFNGLMAPBUFFERPROC fMapBuffer; - typedef realGLboolean (GLAPIENTRY * PFNGLUNAMPBUFFERPROC) (GLenum target); - PFNGLUNAMPBUFFERPROC fUnmapBuffer; -#endif typedef void (GLAPIENTRY * PFNGLPIXELSTOREIPROC) (GLenum pname, GLint param); PFNGLPIXELSTOREIPROC fPixelStorei; typedef void (GLAPIENTRY * PFNGLPOLYGONOFFSETPROC) (GLfloat factor, GLfloat bias); @@ -650,10 +835,6 @@ public: PFNGLBINDRENDERBUFFER fBindRenderbuffer; typedef GLenum (GLAPIENTRY * PFNGLCHECKFRAMEBUFFERSTATUS) (GLenum target); PFNGLCHECKFRAMEBUFFERSTATUS fCheckFramebufferStatus; - typedef void (GLAPIENTRY * PFNGLDELETEFRAMEBUFFERS) (GLsizei n, const GLuint* ids); - PFNGLDELETEFRAMEBUFFERS fDeleteFramebuffers; - typedef void (GLAPIENTRY * PFNGLDELETERENDERBUFFERS) (GLsizei n, const GLuint* ids); - PFNGLDELETERENDERBUFFERS fDeleteRenderbuffers; typedef void (GLAPIENTRY * PFNGLFRAMEBUFFERRENDERBUFFER) (GLenum target, GLenum attachmentPoint, GLenum renderbufferTarget, GLuint renderbuffer); PFNGLFRAMEBUFFERRENDERBUFFER fFramebufferRenderbuffer; typedef void (GLAPIENTRY * PFNGLFRAMEBUFFERTEXTURE2D) (GLenum target, GLenum attachmentPoint, GLenum textureTarget, GLuint texture, GLint level); @@ -662,10 +843,6 @@ public: PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIV fGetFramebufferAttachmentParameteriv; typedef void (GLAPIENTRY * PFNGLGETRENDERBUFFERPARAMETERIV) (GLenum target, GLenum pname, GLint* value); PFNGLGETRENDERBUFFERPARAMETERIV fGetRenderbufferParameteriv; - typedef void (GLAPIENTRY * PFNGLGENFRAMEBUFFERS) (GLsizei n, GLuint* ids); - PFNGLGENFRAMEBUFFERS fGenFramebuffers; - typedef void (GLAPIENTRY * PFNGLGENRENDERBUFFERS) (GLsizei n, GLuint* ids); - PFNGLGENRENDERBUFFERS fGenRenderbuffers; typedef realGLboolean (GLAPIENTRY * PFNGLISFRAMEBUFFER) (GLuint framebuffer); PFNGLISFRAMEBUFFER fIsFramebuffer; typedef realGLboolean (GLAPIENTRY * PFNGLISRENDERBUFFER) (GLuint renderbuffer); @@ -673,6 +850,264 @@ public: typedef void (GLAPIENTRY * PFNGLRENDERBUFFERSTORAGE) (GLenum target, GLenum internalFormat, GLsizei width, GLsizei height); PFNGLRENDERBUFFERSTORAGE fRenderbufferStorage; +protected: + /* These are special -- they create or delete GL resources that can live + * in a shared namespace. In DEBUG, we wrap these calls so that we can + * check when we have something that failed to do cleanup at the time the + * final context is destroyed. + */ + + typedef GLuint (GLAPIENTRY * PFNGLCREATEPROGRAMPROC) (void); + PFNGLCREATEPROGRAMPROC priv_fCreateProgram; + typedef GLuint (GLAPIENTRY * PFNGLCREATESHADERPROC) (GLenum type); + PFNGLCREATESHADERPROC priv_fCreateShader; + typedef void (GLAPIENTRY * PFNGLGENBUFFERSPROC) (GLsizei n, GLuint* buffers); + PFNGLGENBUFFERSPROC priv_fGenBuffers; + typedef void (GLAPIENTRY * PFNGLGENTEXTURESPROC) (GLsizei n, GLuint *textures); + PFNGLGENTEXTURESPROC priv_fGenTextures; + typedef void (GLAPIENTRY * PFNGLGENFRAMEBUFFERS) (GLsizei n, GLuint* ids); + PFNGLGENFRAMEBUFFERS priv_fGenFramebuffers; + typedef void (GLAPIENTRY * PFNGLGENRENDERBUFFERS) (GLsizei n, GLuint* ids); + PFNGLGENRENDERBUFFERS priv_fGenRenderbuffers; + + typedef void (GLAPIENTRY * PFNGLDELETEPROGRAMPROC) (GLuint program); + PFNGLDELETEPROGRAMPROC priv_fDeleteProgram; + typedef void (GLAPIENTRY * PFNGLDELETESHADERPROC) (GLuint shader); + PFNGLDELETESHADERPROC priv_fDeleteShader; + typedef void (GLAPIENTRY * PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint* buffers); + PFNGLDELETEBUFFERSPROC priv_fDeleteBuffers; + typedef void (GLAPIENTRY * PFNGLDELETETEXTURESPROC) (GLsizei n, const GLuint* textures); + PFNGLDELETETEXTURESPROC priv_fDeleteTextures; + typedef void (GLAPIENTRY * PFNGLDELETEFRAMEBUFFERS) (GLsizei n, const GLuint* ids); + PFNGLDELETEFRAMEBUFFERS priv_fDeleteFramebuffers; + typedef void (GLAPIENTRY * PFNGLDELETERENDERBUFFERS) (GLsizei n, const GLuint* ids); + PFNGLDELETERENDERBUFFERS priv_fDeleteRenderbuffers; + +public: +#ifndef DEBUG + GLuint GLAPIENTRY fCreateProgram() { + return priv_fCreateProgram(); + } + + GLuint GLAPIENTRY fCreateShader(GLenum t) { + return priv_fCreateShader(t); + } + + void GLAPIENTRY fGenBuffers(GLsizei n, GLuint* names) { + priv_fGenBuffers(n, names); + } + + void GLAPIENTRY fGenTextures(GLsizei n, GLuint* names) { + priv_fGenTextures(n, names); + } + + void GLAPIENTRY fGenFramebuffers(GLsizei n, GLuint* names) { + priv_fGenFramebuffers(n, names); + } + + void GLAPIENTRY fGenRenderbuffers(GLsizei n, GLuint* names) { + priv_fGenRenderbuffers(n, names); + } + + void GLAPIENTRY fDeleteProgram(GLuint program) { + priv_fDeleteProgram(program); + } + + void GLAPIENTRY fDeleteShader(GLuint shader) { + priv_fDeleteShader(shader); + } + + void GLAPIENTRY fDeleteBuffers(GLsizei n, GLuint *names) { + priv_fDeleteBuffers(n, names); + } + + void GLAPIENTRY fDeleteTextures(GLsizei n, GLuint *names) { + priv_fDeleteTextures(n, names); + } + + void GLAPIENTRY fDeleteFramebuffers(GLsizei n, GLuint *names) { + priv_fDeleteFramebuffers(n, names); + } + + void GLAPIENTRY fDeleteRenderbuffers(GLsizei n, GLuint *names) { + priv_fDeleteRenderbuffers(n, names); + } +#else + GLuint GLAPIENTRY fCreateProgram() { + GLuint ret = priv_fCreateProgram(); + if (mSharedContext) { + GLContext *tip = mSharedContext; + while (tip->mSharedContext) + tip = tip->mSharedContext; + tip->CreatedProgram(this, ret); + } + return ret; + } + + GLuint GLAPIENTRY fCreateShader(GLenum t) { + GLuint ret = priv_fCreateShader(t); + if (mSharedContext) { + GLContext *tip = mSharedContext; + while (tip->mSharedContext) + tip = tip->mSharedContext; + tip->CreatedShader(this, ret); + } + return ret; + } + + void GLAPIENTRY fGenBuffers(GLsizei n, GLuint* names) { + priv_fGenBuffers(n, names); + if (mSharedContext) { + GLContext *tip = mSharedContext; + while (tip->mSharedContext) + tip = tip->mSharedContext; + tip->CreatedBuffers(this, n, names); + } + } + + void GLAPIENTRY fGenTextures(GLsizei n, GLuint* names) { + priv_fGenTextures(n, names); + if (mSharedContext) { + GLContext *tip = mSharedContext; + while (tip->mSharedContext) + tip = tip->mSharedContext; + tip->CreatedTextures(this, n, names); + } + } + + void GLAPIENTRY fGenFramebuffers(GLsizei n, GLuint* names) { + priv_fGenFramebuffers(n, names); + if (mSharedContext) { + GLContext *tip = mSharedContext; + while (tip->mSharedContext) + tip = tip->mSharedContext; + tip->CreatedFramebuffers(this, n, names); + } + } + + void GLAPIENTRY fGenRenderbuffers(GLsizei n, GLuint* names) { + priv_fGenRenderbuffers(n, names); + if (mSharedContext) { + GLContext *tip = mSharedContext; + while (tip->mSharedContext) + tip = tip->mSharedContext; + tip->CreatedRenderbuffers(this, n, names); + } + } + + void GLAPIENTRY fDeleteProgram(GLuint program) { + priv_fDeleteProgram(program); + if (mSharedContext) { + GLContext *tip = mSharedContext; + while (tip->mSharedContext) + tip = tip->mSharedContext; + tip->DeletedProgram(this, program); + } + } + + void GLAPIENTRY fDeleteShader(GLuint shader) { + priv_fDeleteShader(shader); + if (mSharedContext) { + GLContext *tip = mSharedContext; + while (tip->mSharedContext) + tip = tip->mSharedContext; + tip->DeletedShader(this, shader); + } + } + + void GLAPIENTRY fDeleteBuffers(GLsizei n, GLuint *names) { + priv_fDeleteBuffers(n, names); + if (mSharedContext) { + GLContext *tip = mSharedContext; + while (tip->mSharedContext) + tip = tip->mSharedContext; + tip->DeletedBuffers(this, n, names); + } + } + + void GLAPIENTRY fDeleteTextures(GLsizei n, GLuint *names) { + priv_fDeleteTextures(n, names); + if (mSharedContext) { + GLContext *tip = mSharedContext; + while (tip->mSharedContext) + tip = tip->mSharedContext; + tip->DeletedTextures(this, n, names); + } + } + + void GLAPIENTRY fDeleteFramebuffers(GLsizei n, GLuint *names) { + priv_fDeleteFramebuffers(n, names); + if (mSharedContext) { + GLContext *tip = mSharedContext; + while (tip->mSharedContext) + tip = tip->mSharedContext; + tip->DeletedFramebuffers(this, n, names); + } + + } + + void GLAPIENTRY fDeleteRenderbuffers(GLsizei n, GLuint *names) { + priv_fDeleteRenderbuffers(n, names); + if (mSharedContext) { + GLContext *tip = mSharedContext; + while (tip->mSharedContext) + tip = tip->mSharedContext; + tip->DeletedRenderbuffers(this, n, names); + } + } + + void CreatedProgram(GLContext *aOrigin, GLuint aName); + void CreatedShader(GLContext *aOrigin, GLuint aName); + void CreatedBuffers(GLContext *aOrigin, GLsizei aCount, GLuint *aNames); + void CreatedTextures(GLContext *aOrigin, GLsizei aCount, GLuint *aNames); + void CreatedFramebuffers(GLContext *aOrigin, GLsizei aCount, GLuint *aNames); + void CreatedRenderbuffers(GLContext *aOrigin, GLsizei aCount, GLuint *aNames); + void DeletedProgram(GLContext *aOrigin, GLuint aName); + void DeletedShader(GLContext *aOrigin, GLuint aName); + void DeletedBuffers(GLContext *aOrigin, GLsizei aCount, GLuint *aNames); + void DeletedTextures(GLContext *aOrigin, GLsizei aCount, GLuint *aNames); + void DeletedFramebuffers(GLContext *aOrigin, GLsizei aCount, GLuint *aNames); + void DeletedRenderbuffers(GLContext *aOrigin, GLsizei aCount, GLuint *aNames); + + void SharedContextDestroyed(GLContext *aChild); + void ReportOutstandingNames(); + + struct NamedResource { + NamedResource() + : origin(nsnull), name(0), originDeleted(PR_FALSE) + { } + + NamedResource(GLContext *aOrigin, GLuint aName) + : origin(aOrigin), name(aName), originDeleted(PR_FALSE) + { } + + GLContext *origin; + GLuint name; + PRBool originDeleted; + + // for sorting + bool operator<(const NamedResource& aOther) const { + if (intptr_t(origin) < intptr_t(aOther.origin)) + return true; + if (name < aOther.name) + return true; + return false; + } + bool operator==(const NamedResource& aOther) const { + return origin == aOther.origin && + name == aOther.name && + originDeleted == aOther.originDeleted; + } + }; + + nsTArray mTrackedPrograms; + nsTArray mTrackedShaders; + nsTArray mTrackedTextures; + nsTArray mTrackedFramebuffers; + nsTArray mTrackedRenderbuffers; + nsTArray mTrackedBuffers; +#endif + }; inline void diff --git a/gfx/thebes/GLContextProvider.h b/gfx/thebes/GLContextProvider.h index ed67ebfc478..fdd48ae164b 100644 --- a/gfx/thebes/GLContextProvider.h +++ b/gfx/thebes/GLContextProvider.h @@ -48,56 +48,6 @@ class gfxASurface; namespace mozilla { namespace gl { -struct THEBES_API ContextFormat { - static const ContextFormat BasicRGBA32Format; - - enum StandardContextFormat { - Empty, - BasicRGBA32, - StrictBasicRGBA32, - BasicRGBX32, - StrictBasicRGBX32 - }; - - ContextFormat(const StandardContextFormat cf) { - memset(this, 0, sizeof(ContextFormat)); - - switch (cf) { - case BasicRGBA32: - red = green = blue = alpha = 8; - minRed = minGreen = minBlue = minAlpha = 1; - break; - - case StrictBasicRGBA32: - red = green = blue = alpha = 8; - minRed = minGreen = minBlue = minAlpha = 8; - break; - - case BasicRGBX32: - red = green = blue = 8; - minRed = minGreen = minBlue = 1; - break; - - case StrictBasicRGBX32: - red = green = blue = alpha = 8; - minRed = minGreen = minBlue = 8; - break; - - default: - break; - } - } - - int depth, minDepth; - int stencil, minStencil; - int red, minRed; - int green, minGreen; - int blue, minBlue; - int alpha, minAlpha; - - int colorBits() const { return red + green + blue; } -}; - #define IN_GL_CONTEXT_PROVIDER_H // Null and OSMesa are always there diff --git a/gfx/thebes/GLContextProviderCGL.mm b/gfx/thebes/GLContextProviderCGL.mm index be200d3d54d..6ca4d0366f3 100644 --- a/gfx/thebes/GLContextProviderCGL.mm +++ b/gfx/thebes/GLContextProviderCGL.mm @@ -50,7 +50,11 @@ namespace gl { class CGLLibrary { public: - CGLLibrary() : mInitialized(PR_FALSE), mOGLLibrary(nsnull) {} + CGLLibrary() + : mInitialized(PR_FALSE), + mOGLLibrary(nsnull), + mPixelFormat(nsnull) + { } PRBool EnsureInitialized() { @@ -69,34 +73,68 @@ public: return PR_TRUE; } + NSOpenGLPixelFormat *PixelFormat() + { + if (mPixelFormat == nsnull) { + NSOpenGLPixelFormatAttribute attribs[] = { + NSOpenGLPFAAccelerated, + (NSOpenGLPixelFormatAttribute)nil + }; + + mPixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs]; + } + + return mPixelFormat; + } private: PRBool mInitialized; PRLibrary *mOGLLibrary; + NSOpenGLPixelFormat *mPixelFormat; }; CGLLibrary sCGLLibrary; class GLContextCGL : public GLContext { + friend class GLContextProviderCGL; + public: - GLContextCGL(NSOpenGLContext *aContext) - : mContext(aContext), mCGLContext(nsnull), mPBuffer(nsnull) + GLContextCGL(const ContextFormat& aFormat, + GLContext *aShareContext, + NSOpenGLContext *aContext, + PRBool aIsOffscreen = PR_FALSE) + : GLContext(aFormat, aIsOffscreen, aShareContext), + mContext(aContext), + mPBuffer(nsnull), + mTempTextureName(0) { } - GLContextCGL(CGLContextObj aContext, CGLPBufferObj aPBuffer) - : mContext(nsnull), mCGLContext(aContext), mPBuffer(aPBuffer) + GLContextCGL(const ContextFormat& aFormat, + GLContext *aShareContext, + NSOpenGLContext *aContext, + NSOpenGLPixelBuffer *aPixelBuffer) + : GLContext(aFormat, PR_TRUE, aShareContext), + mContext(aContext), + mPBuffer(aPixelBuffer), + mTempTextureName(0) { } ~GLContextCGL() { + if (mOffscreenFBO) { + MakeCurrent(); + DeleteOffscreenFBO(); + } + if (mContext) [mContext release]; - if (mCGLContext) - CGLDestroyContext(mCGLContext); - if (mPBuffer) - CGLDestroyPBuffer(mPBuffer); + [mPBuffer release]; + } + + GLContextType GetContextType() { + return ContextTypeCGL; } PRBool Init() @@ -111,12 +149,6 @@ public: case NativeGLContext: return mContext; - case NativeCGLContext: - return mCGLContext ? mCGLContext : [mContext CGLContextObj]; - - case NativePBuffer: - return mPBuffer; - default: return nsnull; } @@ -126,8 +158,6 @@ public: { if (mContext) { [mContext makeCurrentContext]; - } else if (mCGLContext) { - CGLSetCurrentContext(mCGLContext); } return PR_TRUE; } @@ -137,18 +167,104 @@ public: return PR_FALSE; } + PRBool BindTex2DOffscreen(GLContext *aOffscreen); + void UnbindTex2DOffscreen(GLContext *aOffscreen); + PRBool ResizeOffscreen(const gfxIntSize& aNewSize); + virtual already_AddRefed CreateBasicTextureImage(GLuint aTexture, const nsIntSize& aSize, TextureImage::ContentType aContentType, GLContext* aContext); -private: NSOpenGLContext *mContext; - CGLContextObj mCGLContext; - CGLPBufferObj mPBuffer; + NSOpenGLPixelBuffer *mPBuffer; + GLuint mTempTextureName; }; +PRBool +GLContextCGL::BindTex2DOffscreen(GLContext *aOffscreen) +{ + if (aOffscreen->GetContextType() != ContextTypeCGL) { + NS_WARNING("non-CGL context"); + return PR_FALSE; + } + + if (!aOffscreen->IsOffscreen()) { + NS_WARNING("non-offscreen context"); + return PR_FALSE; + } + + GLContextCGL *offs = static_cast(aOffscreen); + + if (offs->mPBuffer) { + fGenTextures(1, &mTempTextureName); + fBindTexture(LOCAL_GL_TEXTURE_2D, mTempTextureName); + + [mContext + setTextureImageToPixelBuffer:offs->mPBuffer + colorBuffer:LOCAL_GL_FRONT]; + } else if (offs->mOffscreenTexture) { + if (offs->GetSharedContext() != GLContextProviderCGL::GetGlobalContext()) + { + NS_WARNING("offscreen FBO context can only be bound with context sharing!"); + return PR_FALSE; + } + + fBindTexture(LOCAL_GL_TEXTURE_2D, offs->mOffscreenTexture); + } else { + NS_WARNING("don't know how to bind this!"); + return PR_FALSE; + } + + return PR_TRUE; +} + +void +GLContextCGL::UnbindTex2DOffscreen(GLContext *aOffscreen) +{ + NS_ASSERTION(aOffscreen->GetContextType() == ContextTypeCGL, "wrong type"); + + GLContextCGL *offs = static_cast(aOffscreen); + if (offs->mPBuffer) { + NS_ASSERTION(mTempTextureName, "We didn't have an offscreen texture name?"); + fDeleteTextures(1, &mTempTextureName); + mTempTextureName = 0; + } +} + +PRBool +GLContextCGL::ResizeOffscreen(const gfxIntSize& aNewSize) +{ + if (mPBuffer) { + NSOpenGLPixelBuffer *pb = [[NSOpenGLPixelBuffer alloc] + initWithTextureTarget:LOCAL_GL_TEXTURE_2D + textureInternalFormat:(mCreationFormat.alpha ? LOCAL_GL_RGBA : LOCAL_GL_RGB) + textureMaxMipMapLevel:0 + pixelsWide:aNewSize.width + pixelsHigh:aNewSize.height]; + if (!pb) { + return PR_FALSE; + } + + [mPBuffer release]; + mPBuffer = pb; + + mOffscreenSize = aNewSize; + mOffscreenActualSize = aNewSize; + + [mContext setPixelBuffer:pb cubeMapFace:0 mipMapLevel:0 + currentVirtualScreen:[mContext currentVirtualScreen]]; + + MakeCurrent(); + ClearSafely(); + + return PR_TRUE; + } + + return ResizeOffscreenFBO(aNewSize); +} + class TextureImageCGL : public BasicTextureImage { friend already_AddRefed @@ -204,112 +320,176 @@ GLContextCGL::CreateBasicTextureImage(GLuint aTexture, return teximage.forget(); } +static GLContextCGL * +GetGlobalContextCGL() +{ + return static_cast(GLContextProviderCGL::GetGlobalContext()); +} + already_AddRefed GLContextProviderCGL::CreateForWindow(nsIWidget *aWidget) { if (!sCGLLibrary.EnsureInitialized()) { return nsnull; } - NSOpenGLPixelFormatAttribute attributes [] = { - NSOpenGLPFAAccelerated, - (NSOpenGLPixelFormatAttribute)nil - }; - - NSOpenGLPixelFormat *pixelFormat = [[(NSOpenGLPixelFormat *)[NSOpenGLPixelFormat alloc] - initWithAttributes:attributes] - autorelease]; - NSOpenGLContext *context = [[NSOpenGLContext alloc] - initWithFormat:pixelFormat - shareContext:NULL]; - if (context == nil) { + GLContextCGL *shareContext = GetGlobalContextCGL(); + + NSOpenGLContext *context = [[NSOpenGLContext alloc] + initWithFormat:sCGLLibrary.PixelFormat() + shareContext:(shareContext ? shareContext->mContext : NULL)]; + if (!context) { return nsnull; } - nsRefPtr glContext = new GLContextCGL(context); + NSView *childView = (NSView *)aWidget->GetNativeData(NS_NATIVE_WIDGET); + [context setView:childView]; + + nsRefPtr glContext = new GLContextCGL(ContextFormat(ContextFormat::BasicRGB24), + shareContext, + context); if (!glContext->Init()) { return nsnull; - } - - NSView *childView = (NSView *)aWidget->GetNativeData(NS_NATIVE_WIDGET); - if ([context view] != childView) { - [context setView:childView]; - } + } - return glContext.forget().get(); + return glContext.forget(); } -already_AddRefed -GLContextProviderCGL::CreatePBuffer(const gfxIntSize &aSize, - const ContextFormat &aFormat) +static already_AddRefed +CreateOffscreenPBufferContext(const gfxIntSize& aSize, + const ContextFormat& aFormat, + PRBool aShare = PR_FALSE) { if (!sCGLLibrary.EnsureInitialized()) { return nsnull; } - nsTArray attribs; - -#define A1_(_x) do { \ - attribs.AppendElement((CGLPixelFormatAttribute) _x); \ - } while(0) -#define A2_(_x,_y) do { \ - attribs.AppendElement((CGLPixelFormatAttribute) _x); \ - attribs.AppendElement((CGLPixelFormatAttribute) _y); \ - } while(0) - - A1_(kCGLPFAAccelerated); - A1_(kCGLPFAMinimumPolicy); - A1_(kCGLPFAPBuffer); - - A2_(kCGLPFAColorSize, aFormat.colorBits()); - A2_(kCGLPFAAlphaSize, aFormat.alpha); - A2_(kCGLPFADepthSize, aFormat.depth); - - A1_(0); - - CGLError err; - - GLint nFormats; - CGLPixelFormatObj pixelFormat; - CGLContextObj context; - CGLPBufferObj pbuffer; - GLint screen; - - err = CGLChoosePixelFormat(attribs.Elements(), &pixelFormat, &nFormats); - if (err) { + GLContextCGL *shareContext = aShare ? GetGlobalContextCGL() : nsnull; + if (aShare && !shareContext) { return nsnull; } - err = CGLCreateContext(pixelFormat, NULL, &context); - if (err) { + nsTArray attribs; + +#define A_(_x) attribs.AppendElement(NSOpenGLPixelFormatAttribute(_x)) + A_(NSOpenGLPFAAccelerated); + A_(NSOpenGLPFAPixelBuffer); + A_(NSOpenGLPFAMinimumPolicy); + + A_(NSOpenGLPFAColorSize); + A_(aFormat.colorBits()); + + A_(NSOpenGLPFAAlphaSize); + A_(aFormat.alpha); + + A_(NSOpenGLPFADepthSize); + A_(aFormat.depth); + + A_(NSOpenGLPFAStencilSize); + A_(aFormat.stencil); + + A_(0); +#undef A_ + + printf_stderr("colorbits: %d alpha: %d depth: %d stencil: %d\n", aFormat.colorBits(), aFormat.alpha, aFormat.depth, aFormat.stencil); + + NSOpenGLPixelFormat *pbFormat = [[NSOpenGLPixelFormat alloc] + initWithAttributes:attribs.Elements()]; + if (!pbFormat) { return nsnull; } - err = CGLCreatePBuffer(aSize.width, aSize.height, LOCAL_GL_TEXTURE_2D, - LOCAL_GL_RGBA, - 0, &pbuffer); - if (err) { + NSOpenGLPixelBuffer *pb = [[NSOpenGLPixelBuffer alloc] + initWithTextureTarget:LOCAL_GL_TEXTURE_2D + textureInternalFormat:(aFormat.alpha ? LOCAL_GL_RGBA : LOCAL_GL_RGB) + textureMaxMipMapLevel:0 + pixelsWide:aSize.width + pixelsHigh:aSize.height]; + if (!pb) { + [pbFormat release]; return nsnull; } - err = CGLGetVirtualScreen(context, &screen); - if (err) { + NSOpenGLContext *context = [[NSOpenGLContext alloc] + initWithFormat:pbFormat + shareContext:shareContext ? shareContext->mContext : NULL]; + if (!context) { + [pbFormat release]; + [pb release]; return nsnull; } - err = CGLSetPBuffer(context, pbuffer, 0, 0, screen); - if (err) { + [context + setPixelBuffer:pb + cubeMapFace:0 + mipMapLevel:0 + currentVirtualScreen:[context currentVirtualScreen]]; + + { + GLint l; + [pbFormat getValues:&l forAttribute:NSOpenGLPFADepthSize forVirtualScreen:[context currentVirtualScreen]]; + printf_stderr("*** depth: %d (req: %d)\n", l, aFormat.depth); + } + + [pbFormat release]; + + nsRefPtr glContext = new GLContextCGL(aFormat, shareContext, context, pb); + return glContext.forget(); +} + +static already_AddRefed +CreateOffscreenFBOContext(const gfxIntSize& aSize, + const ContextFormat& aFormat, + PRBool aShare = PR_TRUE) +{ + if (!sCGLLibrary.EnsureInitialized()) { return nsnull; } - CGLDestroyPixelFormat(pixelFormat); - - nsRefPtr glContext = new GLContextCGL(context, pbuffer); - if (!glContext->Init()) { + GLContextCGL *shareContext = aShare ? GetGlobalContextCGL() : nsnull; + if (aShare && !shareContext) { + // if there is no share context, then we can't use FBOs. return nsnull; } - return glContext.forget().get(); + NSOpenGLContext *context = [[NSOpenGLContext alloc] + initWithFormat:sCGLLibrary.PixelFormat() + shareContext:shareContext ? shareContext->mContext : NULL]; + if (!context) { + return nsnull; + } + + nsRefPtr glContext = new GLContextCGL(aFormat, shareContext, context, PR_TRUE); + return glContext.forget(); +} + +already_AddRefed +GLContextProviderCGL::CreateOffscreen(const gfxIntSize& aSize, + const ContextFormat& aFormat) +{ + nsRefPtr glContext; + + glContext = CreateOffscreenPBufferContext(aSize, aFormat); + if (glContext && + glContext->Init()) + { + glContext->mOffscreenSize = aSize; + glContext->mOffscreenActualSize = aSize; + + return glContext.forget(); + } + + // try a FBO as second choice + glContext = CreateOffscreenFBOContext(aSize, aFormat); + if (glContext && + glContext->Init() && + glContext->ResizeOffscreenFBO(aSize)) + { + return glContext.forget(); + } + + // everything failed + return nsnull; } already_AddRefed @@ -318,5 +498,27 @@ GLContextProviderCGL::CreateForNativePixmapSurface(gfxASurface *aSurface) return nsnull; } +static nsRefPtr gGlobalContext; + +GLContext * +GLContextProviderCGL::GetGlobalContext() +{ + if (!sCGLLibrary.EnsureInitialized()) { + return nsnull; + } + + if (!gGlobalContext) { + // There are bugs in some older drivers with pbuffers less + // than 16x16 in size; also 16x16 is POT so that we can do + // a FBO with it on older video cards. A FBO context for + // sharing is preferred since it has no associated target. + gGlobalContext = CreateOffscreenFBOContext(gfxIntSize(16, 16), + ContextFormat(ContextFormat::BasicRGB24), + PR_FALSE); + } + + return gGlobalContext; +} + } /* namespace gl */ } /* namespace mozilla */ diff --git a/gfx/thebes/GLContextProviderEGL.cpp b/gfx/thebes/GLContextProviderEGL.cpp index 11052d46606..6150ffbfff5 100644 --- a/gfx/thebes/GLContextProviderEGL.cpp +++ b/gfx/thebes/GLContextProviderEGL.cpp @@ -55,6 +55,7 @@ #include #include +#include "mozilla/X11Util.h" #include "gfxXlibSurface.h" typedef Display *EGLNativeDisplayType; typedef Pixmap EGLNativePixmapType; @@ -65,9 +66,6 @@ typedef Window EGLNativeWindowType; #elif defined(ANDROID) -#include -#define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gecko" , ## args) - /* from widget */ #include "AndroidBridge.h" @@ -111,10 +109,43 @@ typedef void *GLeglImageOES; #define EGL_NO_DISPLAY ((EGLDisplay)0) #define EGL_NO_SURFACE ((EGLSurface)0) +#define EGL_DISPLAY() sEGLLibrary.Display() + +static int +next_power_of_two(int v) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + + return v; +} + +static bool +is_power_of_two(int v) +{ + NS_ASSERTION(v >= 0, "bad value"); + + if (v == 0) + return true; + + return (v & (v-1)) == 0; +} + static class EGLLibrary { public: - EGLLibrary() : mInitialized(PR_FALSE) {} + EGLLibrary() + : mInitialized(PR_FALSE) + { + mHave_EGL_KHR_image_base = PR_FALSE; + mHave_EGL_KHR_image_pixmap = PR_FALSE; + mHave_EGL_KHR_gl_texture_2D_image = PR_FALSE; + } typedef EGLDisplay (*pfnGetDisplay)(void *display_id); pfnGetDisplay fGetDisplay; @@ -217,6 +248,42 @@ public: return PR_FALSE; } + mEGLDisplay = fGetDisplay(EGL_DEFAULT_DISPLAY); + if (!fInitialize(mEGLDisplay, NULL, NULL)) + return PR_FALSE; + + const char *extensions = (const char*) fQueryString(mEGLDisplay, LOCAL_EGL_EXTENSIONS); + + // note the extra space -- this ugliness tries to match + // EGL_KHR_image in the middle of the string, or right at the + // end. It's a prefix for other extensions, so we have to do + // this... + PRBool hasKHRImage; + if (strstr(extensions, "EGL_KHR_image ") || + (strlen(extensions) >= strlen("EGL_KHR_image") && + strcmp(extensions+(strlen(extensions)-strlen("EGL_KHR_image")), "EGL_KHR_image"))) + { + hasKHRImage = PR_TRUE; + } + + if (strstr(extensions, "EGL_KHR_image_base")) { + mHave_EGL_KHR_image_base = PR_TRUE; + } + + if (strstr(extensions, "EGL_KHR_image_pixmap")) { + mHave_EGL_KHR_image_pixmap = PR_TRUE; + + } + + if (strstr(extensions, "EGL_KHR_gl_texture_2D_image")) { + mHave_EGL_KHR_gl_texture_2D_image = PR_TRUE; + } + + if (hasKHRImage) { + mHave_EGL_KHR_image_base = PR_TRUE; + mHave_EGL_KHR_image_pixmap = PR_TRUE; + } + LibrarySymbolLoader::SymLoadStruct khrSymbols[] = { { (PRFuncPtr*) &fCreateImageKHR, { "eglCreateImageKHR", NULL } }, { (PRFuncPtr*) &fDestroyImageKHR, { "eglDestroyImageKHR", NULL } }, @@ -224,20 +291,101 @@ public: { NULL, { NULL } } }; - if (!LibrarySymbolLoader::LoadSymbols(mEGLLibrary, &khrSymbols[0], - (LibrarySymbolLoader::PlatformLookupFunction)fGetProcAddress)) - { - // just means that EGL_image_* isn't supported - fCreateImageKHR = nsnull; + LibrarySymbolLoader::LoadSymbols(mEGLLibrary, &khrSymbols[0], + (LibrarySymbolLoader::PlatformLookupFunction)fGetProcAddress); + + if (!fCreateImageKHR) { + mHave_EGL_KHR_image_base = PR_FALSE; + mHave_EGL_KHR_image_pixmap = PR_FALSE; + mHave_EGL_KHR_gl_texture_2D_image = PR_FALSE; + } + + if (!fImageTargetTexture2DOES) { + mHave_EGL_KHR_gl_texture_2D_image = PR_FALSE; } mInitialized = PR_TRUE; return PR_TRUE; } + EGLDisplay Display() { + return mEGLDisplay; + } + + PRBool HasKHRImageBase() { + return mHave_EGL_KHR_image_base; + } + + PRBool HasKHRImagePixmap() { + return mHave_EGL_KHR_image_base; + } + + PRBool HasKHRImageTexture2D() { + return mHave_EGL_KHR_gl_texture_2D_image; + } + + void + DumpEGLConfig(EGLConfig cfg) + { + int attrval; + int err; + +#define ATTR(_x) do { \ + fGetConfigAttrib(mEGLDisplay, cfg, LOCAL_EGL_##_x, &attrval); \ + if ((err = fGetError()) != 0x3000) { \ + printf_stderr(" %s: ERROR (0x%04x)", #_x, err); \ + } else { \ + printf_stderr(" %s: %d (0x%04x)", #_x, attrval, attrval); \ + } \ + } while(0) + + printf_stderr("EGL Config: %d [0x%08x]", (int)cfg, (PRUint32)cfg); + + ATTR(BUFFER_SIZE); + ATTR(ALPHA_SIZE); + ATTR(BLUE_SIZE); + ATTR(GREEN_SIZE); + ATTR(RED_SIZE); + ATTR(DEPTH_SIZE); + ATTR(STENCIL_SIZE); + ATTR(CONFIG_CAVEAT); + ATTR(CONFIG_ID); + ATTR(LEVEL); + ATTR(MAX_PBUFFER_HEIGHT); + ATTR(MAX_PBUFFER_PIXELS); + ATTR(MAX_PBUFFER_WIDTH); + ATTR(NATIVE_RENDERABLE); + ATTR(NATIVE_VISUAL_ID); + ATTR(NATIVE_VISUAL_TYPE); + ATTR(PRESERVED_RESOURCES); + ATTR(SAMPLES); + ATTR(SAMPLE_BUFFERS); + ATTR(SURFACE_TYPE); + ATTR(TRANSPARENT_TYPE); + ATTR(TRANSPARENT_RED_VALUE); + ATTR(TRANSPARENT_GREEN_VALUE); + ATTR(TRANSPARENT_BLUE_VALUE); + ATTR(BIND_TO_TEXTURE_RGB); + ATTR(BIND_TO_TEXTURE_RGBA); + ATTR(MIN_SWAP_INTERVAL); + ATTR(MAX_SWAP_INTERVAL); + ATTR(LUMINANCE_SIZE); + ATTR(ALPHA_MASK_SIZE); + ATTR(COLOR_BUFFER_TYPE); + ATTR(RENDERABLE_TYPE); + ATTR(CONFORMANT); + +#undef ATTR + } + private: PRBool mInitialized; PRLibrary *mEGLLibrary; + EGLDisplay mEGLDisplay; + + PRPackedBool mHave_EGL_KHR_image_base; + PRPackedBool mHave_EGL_KHR_image_pixmap; + PRPackedBool mHave_EGL_KHR_gl_texture_2D_image; } sEGLLibrary; class GLContextEGL : public GLContext @@ -245,15 +393,19 @@ class GLContextEGL : public GLContext friend class TextureImageEGL; public: - GLContextEGL(EGLDisplay aDisplay, EGLConfig aConfig, - EGLSurface aSurface, EGLContext aContext, - void *aGLWidget = nsnull, - gfxASurface *aASurface = nsnull) - : mDisplay(aDisplay), mConfig(aConfig) + GLContextEGL(const ContextFormat& aFormat, + GLContext *aShareContext, + EGLConfig aConfig, + EGLSurface aSurface, + EGLContext aContext, + PRBool aIsOffscreen = PR_FALSE) + : GLContext(aFormat, aIsOffscreen, aShareContext) + , mConfig(aConfig) , mSurface(aSurface), mContext(aContext) - , mGLWidget(aGLWidget) - , mASurface(aASurface) + , mGLWidget(nsnull) + , mThebesSurface(nsnull) , mBound(PR_FALSE) + , mIsPBuffer(PR_FALSE) {} ~GLContextEGL() @@ -264,8 +416,12 @@ public: if (mGLWidget) return; - sEGLLibrary.fDestroyContext(mDisplay, mContext); - sEGLLibrary.fDestroySurface(mDisplay, mSurface); + sEGLLibrary.fDestroyContext(EGL_DISPLAY(), mContext); + sEGLLibrary.fDestroySurface(EGL_DISPLAY(), mSurface); + } + + GLContextType GetContextType() { + return ContextTypeEGL; } PRBool Init() @@ -287,7 +443,7 @@ public: if (mBound && !ReleaseTexImage()) return PR_FALSE; - EGLBoolean success = sEGLLibrary.fBindTexImage(mDisplay, + EGLBoolean success = sEGLLibrary.fBindTexImage(EGL_DISPLAY(), (EGLSurface)mSurface, LOCAL_EGL_BACK_BUFFER); if (success == LOCAL_EGL_FALSE) return PR_FALSE; @@ -301,11 +457,13 @@ public: if (!mBound) return PR_TRUE; - if (!mDisplay || !mSurface) + if (!mSurface) return PR_FALSE; EGLBoolean success; - success = sEGLLibrary.fReleaseTexImage(mDisplay, (EGLSurface)mSurface, LOCAL_EGL_BACK_BUFFER); + success = sEGLLibrary.fReleaseTexImage(EGL_DISPLAY(), + (EGLSurface)mSurface, + LOCAL_EGL_BACK_BUFFER); if (success == LOCAL_EGL_FALSE) return PR_FALSE; @@ -313,14 +471,13 @@ public: return PR_TRUE; } - PRBool MakeCurrent() - { + PRBool MakeCurrent(PRBool aForce) { PRBool succeeded = PR_TRUE; // Assume that EGL has the same problem as WGL does, // where MakeCurrent with an already-current context is // still expensive. - if (sEGLLibrary.fGetCurrentContext() != mContext) { + if (aForce || sEGLLibrary.fGetCurrentContext() != mContext) { if (mGLWidget) { #ifdef MOZ_WIDGET_QT static_cast(mGLWidget)->makeCurrent(); @@ -328,7 +485,9 @@ public: succeeded = PR_FALSE; #endif } else { - succeeded = sEGLLibrary.fMakeCurrent(mDisplay, mSurface, mSurface, mContext); + succeeded = sEGLLibrary.fMakeCurrent(EGL_DISPLAY(), + mSurface, mSurface, + mContext); } NS_ASSERTION(succeeded, "Failed to make GL context current!"); } @@ -336,6 +495,11 @@ public: return succeeded; } + PRBool MakeCurrent() + { + return MakeCurrent(PR_FALSE); + } + PRBool SetupLookupFunction() { mLookupFunc = (PlatformLookupFunction)sEGLLibrary.fGetProcAddress; @@ -348,9 +512,6 @@ public: case NativeGLContext: return mContext; - case NativePBuffer: - return mSurface; - default: return nsnull; } @@ -358,7 +519,7 @@ public: PRBool SwapBuffers() { - return sEGLLibrary.fSwapBuffers(mDisplay, mSurface); + return sEGLLibrary.fSwapBuffers(EGL_DISPLAY(), mSurface); } virtual already_AddRefed @@ -367,16 +528,168 @@ public: GLint aWrapMode, PRBool aUseNearestFilter=PR_FALSE); -private: - EGLDisplay mDisplay; + // hold a reference to the given surface + // for the lifetime of this context. + void HoldSurface(gfxASurface *aSurf) { + mThebesSurface = aSurf; + } + + void SetQtGLWidget(void *widget) { + mGLWidget = widget; + } + + void SetIsPBuffer() { + mIsPBuffer = PR_TRUE; + } + + EGLContext Context() { + return mContext; + } + + PRBool BindTex2DOffscreen(GLContext *aOffscreen); + void UnbindTex2DOffscreen(GLContext *aOffscreen); + PRBool ResizeOffscreen(const gfxIntSize& aNewSize); + void BindOffscreenFramebuffer(); + + static already_AddRefed + CreateEGLPixmapOffscreenContext(const gfxIntSize& aSize, + const ContextFormat& aFormat); + + static already_AddRefed + CreateEGLPBufferOffscreenContext(const gfxIntSize& aSize, + const ContextFormat& aFormat); + + void SetOffscreenSize(const gfxIntSize &aRequestedSize, + const gfxIntSize &aActualSize) + { + mOffscreenSize = aRequestedSize; + mOffscreenActualSize = aActualSize; + } + +protected: + friend class GLContextProviderEGL; + EGLConfig mConfig; EGLSurface mSurface; EGLContext mContext; - void *mGLWidget; - nsRefPtr mASurface; - PRBool mBound; + void *mGLWidget; + nsRefPtr mThebesSurface; + PRBool mBound; + + PRPackedBool mIsPBuffer; }; +PRBool +GLContextEGL::BindTex2DOffscreen(GLContext *aOffscreen) +{ + if (aOffscreen->GetContextType() != ContextTypeEGL) { + NS_WARNING("non-EGL context"); + return PR_FALSE; + } + + GLContextEGL *offs = static_cast(aOffscreen); + + if (offs->mIsPBuffer) { + PRBool ok = sEGLLibrary.fBindTexImage(EGL_DISPLAY(), + offs->mSurface, + LOCAL_EGL_BACK_BUFFER); + return ok; + } + + if (offs->mOffscreenTexture) { + if (offs->GetSharedContext() != GLContextProviderEGL::GetGlobalContext()) + { + NS_WARNING("offscreen FBO context can only be bound with context sharing!"); + return PR_FALSE; + } + + fBindTexture(LOCAL_GL_TEXTURE_2D, offs->mOffscreenTexture); + return PR_TRUE; + } + + NS_WARNING("don't know how to bind this!"); + + return PR_FALSE; +} + +void +GLContextEGL::UnbindTex2DOffscreen(GLContext *aOffscreen) +{ + NS_ASSERTION(aOffscreen->GetContextType() == ContextTypeEGL, "wrong type"); + + GLContextEGL *offs = static_cast(aOffscreen); + + if (offs->mIsPBuffer) { + sEGLLibrary.fReleaseTexImage(EGL_DISPLAY(), + offs->mSurface, + LOCAL_EGL_BACK_BUFFER); + } +} + +PRBool +GLContextEGL::ResizeOffscreen(const gfxIntSize& aNewSize) +{ + if (mIsPBuffer) { + EGLint pbattrs[] = { + LOCAL_EGL_WIDTH, 0, + LOCAL_EGL_HEIGHT, 0, + LOCAL_EGL_TEXTURE_TARGET, LOCAL_EGL_TEXTURE_2D, + + LOCAL_EGL_TEXTURE_FORMAT, + mCreationFormat.minAlpha ? + LOCAL_EGL_TEXTURE_RGBA : + LOCAL_EGL_TEXTURE_RGB, + + LOCAL_EGL_NONE + }; + + EGLSurface surface = nsnull; + gfxIntSize pbsize(aNewSize); + +TRY_AGAIN_POWER_OF_TWO: + pbattrs[1] = pbsize.width; + pbattrs[3] = pbsize.height; + + surface = sEGLLibrary.fCreatePbufferSurface(EGL_DISPLAY(), mConfig, pbattrs); + if (!surface) { + if (!is_power_of_two(pbsize.width) || + !is_power_of_two(pbsize.height)) + { + if (!is_power_of_two(pbsize.width)) + pbsize.width = next_power_of_two(pbsize.width); + if (!is_power_of_two(pbsize.height)) + pbsize.height = next_power_of_two(pbsize.height); + + NS_WARNING("Failed to resize pbuffer, trying power of two dims"); + goto TRY_AGAIN_POWER_OF_TWO; + } + + NS_WARNING("Failed to resize pbuffer"); + return nsnull; + } + + SetOffscreenSize(aNewSize, pbsize); + + sEGLLibrary.fDestroySurface(EGL_DISPLAY(), mSurface); + + mSurface = surface; + + MakeCurrent(PR_TRUE); + ClearSafely(); + + return PR_TRUE; + } + + return ResizeOffscreenFBO(aNewSize); +} + + +static GLContextEGL * +GetGlobalContextEGL() +{ + return static_cast(GLContextProviderEGL::GetGlobalContext()); +} + class TextureImageEGL : public TextureImage { public: @@ -402,7 +715,7 @@ public: { NS_ASSERTION(!mUpdateContext, "BeginUpdate() without EndUpdate()?"); - mUpdateContext = new gfxContext(mImpl->mASurface); + mUpdateContext = new gfxContext(mImpl->mThebesSurface); // TextureImageEGL can handle updates to disparate regions // aRegion = aRegion; return mUpdateContext; @@ -445,7 +758,7 @@ GLContextEGL::CreateTextureImage(const nsIntSize& aSize, imageFormat); nsRefPtr impl = - sGLContextProvider.CreateForNativePixmapSurface(pixmap); + GLContextProviderEGL::CreateForNativePixmapSurface(pixmap); if (!impl) // FIXME: should fall back on BasicTextureImage here return NULL; @@ -480,6 +793,7 @@ GLContextProviderEGL::CreateForWindow(nsIWidget *aWidget) } #ifdef MOZ_WIDGET_QT + QWidget *viewport = static_cast(aWidget->GetNativeData(NS_NATIVE_SHELLWIDGET)); if (!viewport) return nsnull; @@ -488,21 +802,26 @@ GLContextProviderEGL::CreateForWindow(nsIWidget *aWidget) // Qt widget viewport already have GL context created by Qt // Create dummy GLContextEGL class nsRefPtr glContext = - new GLContextEGL(NULL, NULL, NULL, - sEGLLibrary.fGetCurrentContext(), - viewport); + new GLContextEGL(ContextFormat(ContextFormat::BasicRGBA32), + NULL, + NULL, NULL, + sEGLLibrary.fGetCurrentContext()); if (!glContext->Init()) return nsnull; - return glContext.forget().get(); - } else { - // All Qt nsIWidget's have the same X-Window surface - // And EGL not allowing to create multiple GL context for the same window - // we should be able to create GL context for QGV viewport once, and reuse it for all child widgets - NS_WARNING("Need special GLContext implementation for QT widgets structure"); - // Switch to software rendering here - return nsnull; + glContext->SetQtGLWidget(viewport); + + return glContext.forget(); } -#endif + + // All Qt nsIWidget's have the same X-Window surface + // And EGL not allowing to create multiple GL context for the same window + // we should be able to create GL context for QGV viewport once, and reuse it for all child widgets + NS_ERROR("Need special GLContext implementation for QT widgets structure"); + + // Switch to software rendering here + return nsnull; + +#else EGLDisplay display; EGLConfig config; @@ -532,13 +851,36 @@ GLContextProviderEGL::CreateForWindow(nsIWidget *aWidget) LOCAL_EGL_NONE }; - EGLint ncfg = 0; - if (!sEGLLibrary.fChooseConfig(display, attribs, &config, 1, &ncfg) || + EGLConfig configs[64]; + EGLint ncfg = 64; + if (!sEGLLibrary.fChooseConfig(display, attribs, configs, ncfg, &ncfg) || ncfg < 1) { return nsnull; } + config = 0; + + for (int i = 0; i < ncfg; ++i) { + EGLint r, g, b; + + sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), configs[i], LOCAL_EGL_RED_SIZE, &r); + sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), configs[i], LOCAL_EGL_GREEN_SIZE, &g); + sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), configs[i], LOCAL_EGL_BLUE_SIZE, &b); + + if (r == 5 && g == 6 && b == 5) { + config = configs[i]; +#ifdef DEBUG + sEGLLibrary.DumpEGLConfig(config); +#endif + break; + } + } + + if (!config) { + return nsnull; + } + #ifdef ANDROID // On Android, we have to ask Java to make the eglCreateWindowSurface // call for us. See GLHelpers.java for a description of why. @@ -566,79 +908,120 @@ GLContextProviderEGL::CreateForWindow(nsIWidget *aWidget) LOCAL_EGL_NONE }; - context = sEGLLibrary.fCreateContext(display, config, 0, cxattribs); + GLContextEGL *shareContext = GetGlobalContextEGL(); + +TRY_AGAIN_NO_SHARING: + context = sEGLLibrary.fCreateContext(display, + config, + shareContext ? shareContext->mContext : EGL_NO_CONTEXT, + cxattribs); if (!context) { + if (shareContext) { + NS_WARNING("CreateForWindow -- couldn't share, trying again"); + shareContext = nsnull; + goto TRY_AGAIN_NO_SHARING; + } + + NS_WARNING("CreateForWindow -- no context, giving up"); sEGLLibrary.fDestroySurface(display, surface); return nsnull; } - nsRefPtr glContext = new GLContextEGL(display, config, surface, context); + nsRefPtr glContext = new GLContextEGL(ContextFormat(ContextFormat::BasicRGB24), + shareContext, + config, surface, context); if (!glContext->Init()) return nsnull; - return glContext.forget().get(); + return glContext.forget(); +#endif } -already_AddRefed -GLContextProviderEGL::CreatePBuffer(const gfxIntSize &aSize, const ContextFormat &aFormat) +already_AddRefed +GLContextEGL::CreateEGLPBufferOffscreenContext(const gfxIntSize& aSize, + const ContextFormat& aFormat) { - if (!sEGLLibrary.EnsureInitialized()) { - return nsnull; - } - - EGLDisplay display; EGLConfig config; EGLSurface surface; EGLContext context; - display = sEGLLibrary.fGetDisplay(EGL_DEFAULT_DISPLAY); - if (!sEGLLibrary.fInitialize(display, NULL, NULL)) - return nsnull; + EGLint attribs[] = { + LOCAL_EGL_RENDERABLE_TYPE, LOCAL_EGL_OPENGL_ES2_BIT, + LOCAL_EGL_SURFACE_TYPE, LOCAL_EGL_PBUFFER_BIT, - nsTArray attribs; + LOCAL_EGL_RED_SIZE, aFormat.red, + LOCAL_EGL_GREEN_SIZE, aFormat.green, + LOCAL_EGL_BLUE_SIZE, aFormat.blue, + LOCAL_EGL_ALPHA_SIZE, aFormat.alpha, + LOCAL_EGL_DEPTH_SIZE, aFormat.minDepth, + LOCAL_EGL_STENCIL_SIZE, aFormat.minStencil, -#define A1_(_x) do { attribs.AppendElement(_x); } while(0) -#define A2_(_x,_y) do { \ - attribs.AppendElement(_x); \ - attribs.AppendElement(_y); \ - } while(0) + aFormat.minAlpha ? + LOCAL_EGL_BIND_TO_TEXTURE_RGBA : + LOCAL_EGL_BIND_TO_TEXTURE_RGB, + LOCAL_EGL_TRUE, - A2_(LOCAL_EGL_RENDERABLE_TYPE, LOCAL_EGL_OPENGL_ES2_BIT); - A2_(LOCAL_EGL_SURFACE_TYPE, LOCAL_EGL_PBUFFER_BIT); - /* fix this for mobile */ - A2_(LOCAL_EGL_BUFFER_SIZE, 15 /*aFormat.colorBits()*/); - A2_(LOCAL_EGL_RED_SIZE, 4 /*aFormat.red*/); - A2_(LOCAL_EGL_GREEN_SIZE, 4 /*aFormat.green*/); - A2_(LOCAL_EGL_BLUE_SIZE, 4 /*aFormat.blue*/); - A2_(LOCAL_EGL_ALPHA_SIZE, aFormat.alpha ? 4 : 0); - A2_(LOCAL_EGL_DEPTH_SIZE, aFormat.depth ? 16 : 0); - A2_(LOCAL_EGL_STENCIL_SIZE, aFormat.stencil); - A1_(LOCAL_EGL_NONE); - - EGLConfig configs[32]; - int numConfigs = 32; - - if (!sEGLLibrary.fChooseConfig(display, attribs.Elements(), - configs, numConfigs, - &numConfigs)) - return nsnull; - - if (numConfigs == 0) - return nsnull; - - // shrug - config = configs[0]; - - EGLint pbattrs[] = { - LOCAL_EGL_WIDTH, aSize.width, - LOCAL_EGL_HEIGHT, aSize.height, LOCAL_EGL_NONE }; - surface = sEGLLibrary.fCreatePbufferSurface(display, config, pbattrs); - if (!surface) + EGLConfig configs[64]; + int numConfigs = 64; + + if (!sEGLLibrary.fChooseConfig(EGL_DISPLAY(), + attribs, + configs, numConfigs, + &numConfigs) + || numConfigs == 0) + { + NS_WARNING("No configs"); + // no configs? no pbuffers! return nsnull; + } + + // XXX do some smarter matching here + config = configs[0]; +#ifdef DEBUG + sEGLLibrary.DumpEGLConfig(config); +#endif + + gfxIntSize pbsize(aSize); + + EGLint pbattrs[] = { + LOCAL_EGL_WIDTH, 0, + LOCAL_EGL_HEIGHT, 0, + + LOCAL_EGL_TEXTURE_TARGET, LOCAL_EGL_TEXTURE_2D, + + LOCAL_EGL_TEXTURE_FORMAT, + aFormat.minAlpha ? + LOCAL_EGL_TEXTURE_RGBA : + LOCAL_EGL_TEXTURE_RGB, + + LOCAL_EGL_NONE + }; + +TRY_AGAIN_POWER_OF_TWO: + pbattrs[1] = pbsize.width; + pbattrs[3] = pbsize.height; + + surface = sEGLLibrary.fCreatePbufferSurface(EGL_DISPLAY(), config, pbattrs); + if (!surface) { + if (!is_power_of_two(pbsize.width) || + !is_power_of_two(pbsize.height)) + { + if (!is_power_of_two(pbsize.width)) + pbsize.width = next_power_of_two(pbsize.width); + if (!is_power_of_two(pbsize.height)) + pbsize.height = next_power_of_two(pbsize.height); + + NS_WARNING("Failed to create pbuffer, trying power of two dims"); + goto TRY_AGAIN_POWER_OF_TWO; + } + + NS_WARNING("Failed to create pbuffer"); + return nsnull; + } sEGLLibrary.fBindAPI(LOCAL_EGL_OPENGL_ES_API); @@ -647,24 +1030,158 @@ GLContextProviderEGL::CreatePBuffer(const gfxIntSize &aSize, const ContextFormat LOCAL_EGL_NONE }; - context = sEGLLibrary.fCreateContext(display, config, EGL_NO_CONTEXT, cxattrs); + context = sEGLLibrary.fCreateContext(EGL_DISPLAY(), + config, + EGL_NO_CONTEXT, + cxattrs); if (!context) { - sEGLLibrary.fDestroySurface(display, surface); + NS_WARNING("Failed to create context"); + sEGLLibrary.fDestroySurface(EGL_DISPLAY(), surface); return nsnull; } - nsRefPtr glContext = new GLContextEGL(display, config, surface, context); + nsRefPtr glContext = new GLContextEGL(aFormat, nsnull, + config, surface, context, + PR_TRUE); - if (!glContext->Init()) + if (!glContext->Init()) { + return nsnull; + } + + glContext->SetOffscreenSize(aSize, pbsize); + + glContext->SetIsPBuffer(); + + return glContext.forget(); +} + +already_AddRefed +GLContextEGL::CreateEGLPixmapOffscreenContext(const gfxIntSize& aSize, + const ContextFormat& aFormat) +{ + // XXX -- write me. + // This needs to find a FBConfig/Visual that matches aFormat, and allocate + // a gfxXlibSurface of the appropriat format, and then create a context + // for it. + // + // The code below is almost correct, except it doesn't do the format-FBConfig + // matching, instead just creating a random gfxXlibSurface. The code below just + // uses context sharing and a FBO target, when instead it should avoid context + // sharing if some form of texture-from-pixmap functionality is available. + + gfxASurface *thebesSurface = nsnull; + EGLNativePixmapType pixmap = 0; + +#ifdef MOZ_X11 + nsRefPtr xsurface = + gfxXlibSurface::Create(DefaultScreenOfDisplay(DefaultXDisplay()), + gfxXlibSurface::FindRenderFormat(DefaultXDisplay(), + gfxASurface::ImageFormatRGB24), + gfxIntSize(16, 16)); + if (xsurface->CairoStatus() != 0) return nsnull; - return glContext.forget().get(); + thebesSurface = xsurface; + pixmap = xsurface->XDrawable(); +#endif + + if (!pixmap) { + return nsnull; + } + + EGLSurface surface; + + EGLConfig configs[32]; + int numConfigs = 32; + EGLConfig config = 0; + + EGLint pixmap_config[] = { + LOCAL_EGL_SURFACE_TYPE, LOCAL_EGL_PIXMAP_BIT, + LOCAL_EGL_RENDERABLE_TYPE, LOCAL_EGL_OPENGL_ES2_BIT, + LOCAL_EGL_DEPTH_SIZE, 0, + LOCAL_EGL_STENCIL_SIZE, 0, + LOCAL_EGL_NONE + }; + + if (!sEGLLibrary.fChooseConfig(EGL_DISPLAY(), pixmap_config, + configs, numConfigs, &numConfigs)) + return nsnull; + + if (numConfigs == 0) + return nsnull; + + for (int i = 0; i < numConfigs; ++i) { + surface = sEGLLibrary.fCreatePixmapSurface(EGL_DISPLAY(), + configs[i], + pixmap, + NULL); + if (surface != EGL_NO_SURFACE) { + config = configs[i]; + break; + } + } + + if (!config) + return nsnull; + + EGLint cxattribs[] = { + LOCAL_EGL_CONTEXT_CLIENT_VERSION, 2, + LOCAL_EGL_NONE + }; + + GLContextEGL *shareContext = GetGlobalContextEGL(); + if (!shareContext) { + // we depend on context sharing currently + sEGLLibrary.fDestroySurface(EGL_DISPLAY(), surface); + return nsnull; + } + + EGLContext context = sEGLLibrary.fCreateContext(EGL_DISPLAY(), + config, + shareContext->Context(), + cxattribs); + if (!context) { + sEGLLibrary.fDestroySurface(EGL_DISPLAY(), surface); + return nsnull; + } + + nsRefPtr glContext = new GLContextEGL(aFormat, shareContext, + config, surface, context, + PR_TRUE); + + if (!glContext->Init() || + !glContext->ResizeOffscreenFBO(aSize)) + return nsnull; + + glContext->HoldSurface(thebesSurface); + + return glContext.forget(); +} + +// Under EGL, if we're under X11, then we have to create a Pixmap +// because Maemo's EGL implementation doesn't support pbuffers at all +// for some reason. On Android, pbuffers are supported fine, though +// often without the ability to texture from them directly. +already_AddRefed +GLContextProviderEGL::CreateOffscreen(const gfxIntSize& aSize, + const ContextFormat& aFormat) +{ + if (!sEGLLibrary.EnsureInitialized()) { + return nsnull; + } + +#if defined(ANDROID) + return GLContextEGL::CreateEGLPBufferOffscreenContext(aSize, aFormat); +#elif defined(MOZ_X11) + return GLContextEGL::CreateEGLPixmapOffscreenContext(aSize, aFormat); +#else + return nsnull; +#endif } already_AddRefed GLContextProviderEGL::CreateForNativePixmapSurface(gfxASurface *aSurface) { - EGLDisplay display = nsnull; EGLSurface surface = nsnull; EGLContext context = nsnull; @@ -679,13 +1196,6 @@ GLContextProviderEGL::CreateForNativePixmapSurface(gfxASurface *aSurface) gfxXlibSurface *xsurface = static_cast(aSurface); - display = sEGLLibrary.fGetDisplay((EGLNativeDisplayType)xsurface->XDisplay()); - if (!display) - return nsnull; - - if (!sEGLLibrary.fInitialize(display, NULL, NULL)) - return nsnull; - EGLConfig configs[32]; int numConfigs = 32; @@ -709,7 +1219,7 @@ GLContextProviderEGL::CreateForNativePixmapSurface(gfxASurface *aSurface) LOCAL_EGL_NONE }; - if (!sEGLLibrary.fChooseConfig(display, pixmap_config, + if (!sEGLLibrary.fChooseConfig(EGL_DISPLAY(), pixmap_config, configs, numConfigs, &numConfigs)) return nsnull; @@ -721,11 +1231,11 @@ GLContextProviderEGL::CreateForNativePixmapSurface(gfxASurface *aSurface) int i = 0; for (i = 0; i < numConfigs; ++i) { if (opaque) - surface = sEGLLibrary.fCreatePixmapSurface(display, configs[i], + surface = sEGLLibrary.fCreatePixmapSurface(EGL_DISPLAY(), configs[i], xsurface->XDrawable(), pixmap_config_rgb); else - surface = sEGLLibrary.fCreatePixmapSurface(display, configs[i], + surface = sEGLLibrary.fCreatePixmapSurface(EGL_DISPLAY(), configs[i], xsurface->XDrawable(), pixmap_config_rgba); @@ -739,22 +1249,44 @@ GLContextProviderEGL::CreateForNativePixmapSurface(gfxASurface *aSurface) LOCAL_EGL_NONE }; - context = sEGLLibrary.fCreateContext(display, configs[i], 0, cxattribs); + context = sEGLLibrary.fCreateContext(EGL_DISPLAY(), + configs[i], + EGL_NO_SURFACE, + cxattribs); if (!context) { - sEGLLibrary.fDestroySurface(display, surface); + sEGLLibrary.fDestroySurface(EGL_DISPLAY(), surface); return nsnull; } nsRefPtr glContext = - new GLContextEGL(display, configs[i], surface, context, NULL, aSurface); + new GLContextEGL(ContextFormat(ContextFormat::BasicRGBA32), + nsnull, configs[i], surface, context, PR_FALSE); + glContext->HoldSurface(xsurface); return glContext.forget().get(); #else + (void)surface; + (void)context; + // Not implemented return nsnull; #endif } +static nsRefPtr gGlobalContext; + +GLContext * +GLContextProviderEGL::GetGlobalContext() +{ + static bool triedToCreateContext = false; + if (!triedToCreateContext && !gGlobalContext) { + triedToCreateContext = true; + gGlobalContext = CreateOffscreen(gfxIntSize(16, 16)); + } + + return gGlobalContext; +} + } /* namespace gl */ } /* namespace mozilla */ diff --git a/gfx/thebes/GLContextProviderGLX.cpp b/gfx/thebes/GLContextProviderGLX.cpp index d36a29990a5..df7c7823aed 100644 --- a/gfx/thebes/GLContextProviderGLX.cpp +++ b/gfx/thebes/GLContextProviderGLX.cpp @@ -53,7 +53,7 @@ #include "nsDebug.h" #include "nsIWidget.h" #include "GLXLibrary.h" -#include "gfxASurface.h" +#include "gfxXlibSurface.h" #include "gfxContext.h" #include "gfxImageSurface.h" #include "gfxPlatform.h" @@ -61,6 +61,10 @@ namespace mozilla { namespace gl { +static PRBool gIsATI = PR_FALSE; +static PRBool gIsChromium = PR_FALSE; +static int gGLXVersion = 0; + PRBool GLXLibrary::EnsureInitialized() { @@ -90,6 +94,10 @@ GLXLibrary::EnsureInitialized() { (PRFuncPtr*) &xGetFBConfigAttrib, { "glXGetFBConfigAttrib", NULL } }, { (PRFuncPtr*) &xSwapBuffers, { "glXSwapBuffers", NULL } }, { (PRFuncPtr*) &xQueryServerString, { "glXQueryServerString", NULL } }, + { (PRFuncPtr*) &xCreatePixmap, { "glXCreatePixmap", NULL } }, + { (PRFuncPtr*) &xDestroyPixmap, { "glXDestroyPixmap", NULL } }, + { (PRFuncPtr*) &xGetClientString, { "glXGetClientString", NULL } }, + { (PRFuncPtr*) &xCreateContext, { "glXCreateContext", NULL } }, { NULL, { NULL } } }; @@ -98,6 +106,40 @@ GLXLibrary::EnsureInitialized() return PR_FALSE; } + const char *vendor = xQueryServerString(DefaultXDisplay(), + DefaultScreen(DefaultXDisplay()), + GLX_VENDOR); + const char *serverVersionStr = xQueryServerString(DefaultXDisplay(), + DefaultScreen(DefaultXDisplay()), + GLX_VERSION); + const char *clientVersionStr = xGetClientString(DefaultXDisplay(), + GLX_VERSION); + + + int serverVersion = 0, clientVersion = 0; + if (serverVersionStr && + strlen(serverVersionStr) > 3 && + serverVersionStr[1] == '.') + { + serverVersion = (serverVersionStr[0] - '0') << 8 | (serverVersionStr[2] - '0'); + } + + if (clientVersion && + strlen(clientVersionStr) > 3 && + clientVersionStr[1] == '.') + { + clientVersion = (clientVersionStr[0] - '0') << 8 | (clientVersionStr[2] - '0'); + } + + gGLXVersion = PR_MIN(clientVersion, serverVersion); + + if (gGLXVersion < 0x0103) + return PR_FALSE; + + gIsATI = vendor && strstr(vendor, "ATI"); + gIsChromium = (vendor && strstr(vendor, "Chromium")) || + (serverVersion && strstr(serverVersionStr, "Chromium")); + mInitialized = PR_TRUE; return PR_TRUE; } @@ -116,7 +158,14 @@ class GLContextGLX : public GLContext { public: static already_AddRefed - CreateGLContext(Display *display, GLXDrawable drawable, GLXFBConfig cfg, PRBool pbuffer) + CreateGLContext(const ContextFormat& format, + Display *display, + GLXDrawable drawable, + GLXFBConfig cfg, + XVisualInfo *vinfo, + GLContextGLX *shareContext, + PRBool deleteDrawable, + gfxXlibSurface *pixmap = nsnull) { int db = 0, err; err = sGLXLibrary.xGetFBConfigAttrib(display, cfg, @@ -128,27 +177,45 @@ public: } ctxErrorOccurred = false; - int (*oldHandler)(Display *, XErrorEvent *) = XSetErrorHandler(&ctxErrorHandler); + int (*oldHandler)(Display *, XErrorEvent *); + GLXContext context; - GLXContext context = sGLXLibrary.xCreateNewContext(display, - cfg, - GLX_RGBA_TYPE, - NULL, - True); +TRY_AGAIN_NO_SHARING: + oldHandler = XSetErrorHandler(&ctxErrorHandler); + + if (gGLXVersion >= 0x0103) { + context = sGLXLibrary.xCreateNewContext(display, + cfg, + GLX_RGBA_TYPE, + shareContext ? shareContext->mContext : NULL, + True); + } else { + context = sGLXLibrary.xCreateContext(display, + vinfo, + shareContext ? shareContext->mContext : NULL, + True); + } XSync(display, False); XSetErrorHandler(oldHandler); if (!context || ctxErrorOccurred) { + if (shareContext) { + shareContext = nsnull; + goto TRY_AGAIN_NO_SHARING; + } NS_WARNING("Failed to create GLXContext!"); return nsnull; } - nsRefPtr glContext(new GLContextGLX(display, + nsRefPtr glContext(new GLContextGLX(format, + shareContext, + display, drawable, context, - pbuffer, - db)); + deleteDrawable, + db, + pixmap)); if (!glContext->Init()) { return nsnull; } @@ -158,11 +225,15 @@ public: ~GLContextGLX() { - if (mPBuffer) { - sGLXLibrary.xDestroyPbuffer(mDisplay, mWindow); - } - sGLXLibrary.xDeleteContext(mDisplay, mContext); + + if (mDeleteDrawable) { + sGLXLibrary.xDestroyPixmap(mDisplay, mDrawable); + } + } + + GLContextType GetContextType() { + return ContextTypeGLX; } PRBool Init() @@ -178,7 +249,7 @@ public: PRBool MakeCurrent() { - Bool succeeded = sGLXLibrary.xMakeCurrent(mDisplay, mWindow, mContext); + Bool succeeded = sGLXLibrary.xMakeCurrent(mDisplay, mDrawable, mContext); NS_ASSERTION(succeeded, "Failed to make GL context current!"); return succeeded; } @@ -195,22 +266,24 @@ public: case NativeGLContext: return mContext; - case NativePBuffer: - if (mPBuffer) { - return (void *)mWindow; - } - // fall through + case NativeThebesSurface: + return mPixmap; default: return nsnull; } } - virtual PRBool SwapBuffers() + PRBool IsDoubleBuffered() { - if (mPBuffer || !mDoubleBuffered) + return mDoubleBuffered; + } + + PRBool SwapBuffers() + { + if (!mDoubleBuffered) return PR_FALSE; - sGLXLibrary.xSwapBuffers(mDisplay, mWindow); + sGLXLibrary.xSwapBuffers(mDisplay, mDrawable); return PR_TRUE; } @@ -249,19 +322,33 @@ public: GLContext* aContext); private: - GLContextGLX(Display *aDisplay, GLXDrawable aWindow, GLXContext aContext, PRBool aPBuffer = PR_FALSE, PRBool aDoubleBuffered=PR_FALSE) - : mContext(aContext), - mDisplay(aDisplay), - mWindow(aWindow), - mPBuffer(aPBuffer), - mDoubleBuffered(aDoubleBuffered) {} + friend class GLContextProviderGLX; + + GLContextGLX(const ContextFormat& aFormat, + GLContext *aShareContext, + Display *aDisplay, + GLXDrawable aDrawable, + GLXContext aContext, + PRBool aDeleteDrawable, + PRBool aDoubleBuffered, + gfxXlibSurface *aPixmap) + : GLContext(aFormat, aDeleteDrawable ? PR_TRUE : PR_FALSE, aShareContext), + mContext(aContext), + mDisplay(aDisplay), + mDrawable(aDrawable), + mDeleteDrawable(aDeleteDrawable), + mDoubleBuffered(aDoubleBuffered), + mPixmap(aPixmap) + { } GLXContext mContext; Display *mDisplay; - GLXDrawable mWindow; - PRBool mPBuffer; - PRBool mDoubleBuffered; + GLXDrawable mDrawable; + PRPackedBool mDeleteDrawable; + PRPackedBool mDoubleBuffered; + nsTArray textures; + nsRefPtr mPixmap; }; // FIXME/bug 575505: this is a (very slow!) placeholder @@ -321,7 +408,14 @@ GLContextGLX::CreateBasicTextureImage(GLuint aTexture, return teximage.forget(); } -static PRBool AreCompatibleVisuals(XVisualInfo *one, XVisualInfo *two) +static GLContextGLX * +GetGlobalContextGLX() +{ + return static_cast(GLContextProviderGLX::GetGlobalContext()); +} + +static PRBool +AreCompatibleVisuals(XVisualInfo *one, XVisualInfo *two) { if (one->c_class != two->c_class) { return PR_FALSE; @@ -362,11 +456,9 @@ GLContextProviderGLX::CreateForWindow(nsIWidget *aWidget) int xscreen = DefaultScreen(display); Window window = GET_NATIVE_WINDOW(aWidget); - const char *vendor = sGLXLibrary.xQueryServerString(display, xscreen, GLX_VENDOR); - PRBool isATI = vendor && strstr(vendor, "ATI"); int numConfigs; ScopedXFree cfgs; - if (isATI) { + if (gIsATI) { const int attribs[] = { GLX_DOUBLEBUFFER, False, 0 @@ -402,7 +494,7 @@ GLContextProviderGLX::CreateForWindow(nsIWidget *aWidget) #endif ScopedXFree vi; - if (isATI) { + if (gIsATI) { XVisualInfo vinfo_template; int nvisuals; vinfo_template.visual = widgetAttrs.visual; @@ -415,18 +507,20 @@ GLContextProviderGLX::CreateForWindow(nsIWidget *aWidget) } int matchIndex = -1; + ScopedXFree vinfo; + for (int i = 0; i < numConfigs; i++) { - ScopedXFree info(sGLXLibrary.xGetVisualFromFBConfig(display, cfgs[i])); - if (!info) { + vinfo = sGLXLibrary.xGetVisualFromFBConfig(display, cfgs[i]); + if (!vinfo) { continue; } - if (isATI) { - if (AreCompatibleVisuals(vi, info)) { + if (gIsATI) { + if (AreCompatibleVisuals(vi, vinfo)) { matchIndex = i; break; } } else { - if (widgetVisualID == info->visualid) { + if (widgetVisualID == vinfo->visualid) { matchIndex = i; break; } @@ -438,21 +532,155 @@ GLContextProviderGLX::CreateForWindow(nsIWidget *aWidget) return nsnull; } - nsRefPtr glContext = GLContextGLX::CreateGLContext(display, + GLContextGLX *shareContext = GetGlobalContextGLX(); + + nsRefPtr glContext = GLContextGLX::CreateGLContext(ContextFormat(ContextFormat::BasicRGB24), + display, window, cfgs[matchIndex], + vinfo, + shareContext, PR_FALSE); return glContext.forget(); } -already_AddRefed -GLContextProviderGLX::CreatePBuffer(const gfxIntSize &aSize, const ContextFormat& aFormat) +static already_AddRefed +CreateOffscreenPixmapContext(const gfxIntSize& aSize, + const ContextFormat& aFormat, + PRBool aShare) { if (!sGLXLibrary.EnsureInitialized()) { return nsnull; } - nsTArray attribs; + Display *display = DefaultXDisplay(); + int xscreen = DefaultScreen(display); + + int attribs[] = { + GLX_DOUBLEBUFFER, False, + GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT, + GLX_X_RENDERABLE, True, + GLX_RED_SIZE, 1, + GLX_GREEN_SIZE, 1, + GLX_BLUE_SIZE, 1, + GLX_ALPHA_SIZE, 0, + GLX_DEPTH_SIZE, 0, + 0 + }; + int numConfigs = 0; + + ScopedXFree cfgs; + cfgs = sGLXLibrary.xChooseFBConfig(display, + xscreen, + attribs, + &numConfigs); + if (!cfgs) { + return nsnull; + } + + NS_ASSERTION(numConfigs > 0, + "glXChooseFBConfig() failed to match our requested format and violated its spec (!)"); + + ScopedXFree vinfo; + int chosenIndex; + + for (int i = 0; i < numConfigs; ++i) { + int dtype, visid; + + if (sGLXLibrary.xGetFBConfigAttrib(display, cfgs[i], GLX_DRAWABLE_TYPE, &dtype) != Success + || !(dtype & GLX_PIXMAP_BIT)) + { + continue; + } + if (sGLXLibrary.xGetFBConfigAttrib(display, cfgs[i], GLX_VISUAL_ID, &visid) != Success + || visid == 0) + { + continue; + } + + vinfo = sGLXLibrary.xGetVisualFromFBConfig(display, cfgs[i]); + + if (vinfo) { + chosenIndex = i; + break; + } + } + + if (!vinfo) { + NS_WARNING("glXChooseFBConfig() didn't give us any configs with visuals!"); + return nsnull; + } + + nsRefPtr xsurface = gfxXlibSurface::Create(DefaultScreenOfDisplay(display), + vinfo->visual, + gfxIntSize(16, 16)); + if (xsurface->CairoStatus() != 0) { + return nsnull; + } + + + GLXPixmap glxpixmap = sGLXLibrary.xCreatePixmap(display, + cfgs[chosenIndex], + xsurface->XDrawable(), + NULL); + if (glxpixmap == 0) { + return nsnull; + } + + GLContextGLX *shareContext = aShare ? GetGlobalContextGLX() : nsnull; + + nsRefPtr glContext = GLContextGLX::CreateGLContext(aFormat, + display, + glxpixmap, + cfgs[chosenIndex], + vinfo, + shareContext, + PR_TRUE, + xsurface); + + return glContext.forget(); +} + +already_AddRefed +GLContextProviderGLX::CreateOffscreen(const gfxIntSize& aSize, + const ContextFormat& aFormat) +{ + + nsRefPtr glContext = + CreateOffscreenPixmapContext(aSize, aFormat, PR_TRUE); + + if (!glContext) { + return nsnull; + } + + if (!glContext->GetSharedContext()) { + // no point in returning anything if sharing failed, we can't + // render from this + return nsnull; + } + + if (!glContext->ResizeOffscreenFBO(aSize)) { + // we weren't able to create the initial + // offscreen FBO, so this is dead + return nsnull; + } + + return glContext.forget(); +} + +already_AddRefed +GLContextProviderGLX::CreateForNativePixmapSurface(gfxASurface *aSurface) +{ + if (!sGLXLibrary.EnsureInitialized()) { + return nsnull; + } + + if (aSurface->GetType() != gfxASurface::SurfaceTypeXlib) { + NS_WARNING("GLContextProviderGLX::CreateForNativePixmapSurface called with non-Xlib surface"); + return nsnull; + } + + nsAutoTArray attribs; #define A1_(_x) do { attribs.AppendElement(_x); } while(0) #define A2_(_x,_y) do { \ @@ -460,20 +688,14 @@ GLContextProviderGLX::CreatePBuffer(const gfxIntSize &aSize, const ContextFormat attribs.AppendElement(_y); \ } while(0) + A2_(GLX_DOUBLEBUFFER, False); + A2_(GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT); + A1_(0); + int numFormats; Display *display = DefaultXDisplay(); int xscreen = DefaultScreen(display); - A2_(GLX_DOUBLEBUFFER, False); - A2_(GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT); - - A2_(GLX_RED_SIZE, aFormat.red); - A2_(GLX_GREEN_SIZE, aFormat.green); - A2_(GLX_BLUE_SIZE, aFormat.blue); - A2_(GLX_ALPHA_SIZE, aFormat.alpha); - A2_(GLX_DEPTH_SIZE, aFormat.depth); - A1_(0); - ScopedXFree cfg(sGLXLibrary.xChooseFBConfig(display, xscreen, attribs.Elements(), @@ -483,35 +705,44 @@ GLContextProviderGLX::CreatePBuffer(const gfxIntSize &aSize, const ContextFormat } NS_ASSERTION(numFormats > 0, "glXChooseFBConfig() failed to match our requested format and violated its spec (!)"); - - nsTArray pbattribs; - pbattribs.AppendElement(GLX_PBUFFER_WIDTH); - pbattribs.AppendElement(aSize.width); - pbattribs.AppendElement(GLX_PBUFFER_HEIGHT); - pbattribs.AppendElement(aSize.height); - pbattribs.AppendElement(GLX_PRESERVED_CONTENTS); - pbattribs.AppendElement(True); - pbattribs.AppendElement(0); - GLXPbuffer pbuffer = sGLXLibrary.xCreatePbuffer(display, + gfxXlibSurface *xs = static_cast(aSurface); + + GLXPixmap glxpixmap = sGLXLibrary.xCreatePixmap(display, cfg[0], - pbattribs.Elements()); + xs->XDrawable(), + NULL); - if (pbuffer == 0) { + nsRefPtr glContext = GLContextGLX::CreateGLContext(ContextFormat(ContextFormat::BasicRGB24), + display, + glxpixmap, + cfg[0], + NULL, + NULL, + PR_FALSE, + xs); + + if (!glContext->Init()) { return nsnull; } - nsRefPtr glContext = GLContextGLX::CreateGLContext(display, - pbuffer, - cfg[0], - PR_TRUE); return glContext.forget(); } -already_AddRefed -GLContextProviderGLX::CreateForNativePixmapSurface(gfxASurface *aSurface) +static nsRefPtr gGlobalContext; + +GLContext * +GLContextProviderGLX::GetGlobalContext() { - return nsnull; + static bool triedToCreateContext = false; + if (!triedToCreateContext && !gGlobalContext) { + triedToCreateContext = true; + gGlobalContext = CreateOffscreenPixmapContext(gfxIntSize(1, 1), + ContextFormat(ContextFormat::BasicRGB24), + PR_FALSE); + } + + return gGlobalContext; } } /* namespace gl */ diff --git a/gfx/thebes/GLContextProviderImpl.h b/gfx/thebes/GLContextProviderImpl.h index b1700f16363..199d4e0bbe7 100644 --- a/gfx/thebes/GLContextProviderImpl.h +++ b/gfx/thebes/GLContextProviderImpl.h @@ -47,33 +47,65 @@ class THEBES_API GL_CONTEXT_PROVIDER_NAME public: /** * Create a context that renders to the surface of the widget that is - * passed in. + * passed in. The context is always created with an RGB pixel format, + * with no alpha, depth or stencil. If any of those features are needed, + * either use a framebuffer, or use CreateOffscreen. * - * @param Widget whose surface to create a context for - * @return Context to use for this window + * This context will attempt to share resources with all other window + * contexts. As such, it's critical that resources allocated that are not + * needed by other contexts be deleted before the context is destroyed. + * + * The GetSharedContext() method will return non-null if sharing + * was successful. + * + * Note: a context created for a widget /must not/ hold a strong + * reference to the widget; otherwise a cycle can be created through + * a GL layer manager. + * + * @param aWidget Widget whose surface to create a context for + * + * @return Context to use for the window */ static already_AddRefed CreateForWindow(nsIWidget *aWidget); /** - * Creates a PBuffer. + * Create a context for offscreen rendering. The target of this + * context should be treated as opaque -- it might be a FBO, or a + * pbuffer, or some other construct. Users of this GLContext + * should not bind framebuffer 0 directly, and instead should bind + * the framebuffer returned by GetOffscreenFBO(). * - * @param aSize Size of the pbuffer to create - * @param aFormat A ContextFormat describing the desired context attributes. Defaults to a basic RGBA32 context. + * The offscreen context returned by this method will always have + * the ability to be rendered into a context created by a window. + * It might or might not share resources with the global context; + * query GetSharedContext() for a non-null result to check. If + * resource sharing can be avoided on the target platform, it will + * be, in order to isolate the offscreen context. * - * @return Context to use for this Pbuffer + * @param aSize The initial size of this offscreen context. + * @param aFormat The ContextFormat for this offscreen context. + * + * @return Context to use for offscreen rendering */ static already_AddRefed - CreatePBuffer(const gfxIntSize &aSize, - const ContextFormat& aFormat = ContextFormat::BasicRGBA32Format); + CreateOffscreen(const gfxIntSize& aSize, + const ContextFormat& aFormat = ContextFormat::BasicRGBA32Format); /** * Try to create a GL context from native surface for arbitrary gfxASurface * If surface not compatible this will return NULL * * @param aSurface surface to create a context for + * * @return Context to use for this surface */ static already_AddRefed CreateForNativePixmapSurface(gfxASurface *aSurface); + + /** + * Get a pointer to the global context, creating it if it doesn't exist. + */ + static GLContext * + GetGlobalContext(); }; diff --git a/gfx/thebes/GLContextProviderNull.cpp b/gfx/thebes/GLContextProviderNull.cpp index d8b0646eda6..b2a83edeca1 100644 --- a/gfx/thebes/GLContextProviderNull.cpp +++ b/gfx/thebes/GLContextProviderNull.cpp @@ -45,13 +45,20 @@ GLContextProviderNull::CreateForWindow(nsIWidget*) } already_AddRefed -GLContextProviderNull::CreateForNativePixmapSurface(gfxASurface *aSurface) +GLContextProviderNull::CreateOffscreen(const gfxIntSize&, + const ContextFormat&) { - return 0; + return nsnull; } already_AddRefed -GLContextProviderNull::CreatePBuffer(const gfxIntSize &, const ContextFormat &) +GLContextProviderNull::CreateForNativePixmapSurface(gfxASurface *) +{ + return nsnull; +} + +GLContext * +GLContextProviderNull::GetGlobalContext() { return nsnull; } diff --git a/gfx/thebes/GLContextProviderOSMesa.cpp b/gfx/thebes/GLContextProviderOSMesa.cpp index ffb30fa76f2..7e66eafbf20 100644 --- a/gfx/thebes/GLContextProviderOSMesa.cpp +++ b/gfx/thebes/GLContextProviderOSMesa.cpp @@ -153,8 +153,9 @@ OSMesaLibrary::EnsureInitialized() class GLContextOSMesa : public GLContext { public: - GLContextOSMesa() - : mThebesSurface(nsnull), + GLContextOSMesa(const ContextFormat& aFormat) + : GLContext(aFormat, PR_TRUE, nsnull), + mThebesSurface(nsnull), mContext(nsnull) { } @@ -165,25 +166,34 @@ public: sOSMesaLibrary.fDestroyContext(mContext); } - PRBool Init(const gfxIntSize &aSize, const ContextFormat& aFormat) + GLContextType GetContextType() { + return ContextTypeOSMesa; + } + + PRBool Init(const gfxIntSize &aSize) { int osmesa_format = -1; int gfxasurface_imageformat = -1; PRBool format_accepted = PR_FALSE; - if (aFormat.red == 8 && aFormat.green == 8 && aFormat.blue == 8) { - if (aFormat.alpha == 8) { - osmesa_format = OSMESA_BGRA; - gfxasurface_imageformat = gfxASurface::ImageFormatARGB32; - format_accepted = PR_TRUE; - } - else if (aFormat.alpha == 0) { + if (mCreationFormat.red > 0 && + mCreationFormat.green > 0 && + mCreationFormat.blue > 0 && + mCreationFormat.red <= 8 && + mCreationFormat.green <= 8 && + mCreationFormat.blue <= 8) + { + if (mCreationFormat.alpha == 0) { // we can't use OSMESA_BGR because it is packed 24 bits per pixel. // So we use OSMESA_BGRA and have to use ImageFormatRGB24 // to make sure that the dummy alpha channel is ignored. osmesa_format = OSMESA_BGRA; gfxasurface_imageformat = gfxASurface::ImageFormatRGB24; format_accepted = PR_TRUE; + } else if (mCreationFormat.alpha <= 8) { + osmesa_format = OSMESA_BGRA; + gfxasurface_imageformat = gfxASurface::ImageFormatARGB32; + format_accepted = PR_TRUE; } } if (!format_accepted) { @@ -197,7 +207,7 @@ public: return PR_FALSE; } - mContext = sOSMesaLibrary.fCreateContextExt(osmesa_format, aFormat.depth, aFormat.stencil, 0, NULL); + mContext = sOSMesaLibrary.fCreateContextExt(osmesa_format, mCreationFormat.depth, mCreationFormat.stencil, 0, NULL); if (!mContext) { NS_WARNING("OSMesaCreateContextExt failed!"); return PR_FALSE; @@ -215,10 +225,10 @@ public: PRBool MakeCurrent() { PRBool succeeded - = sOSMesaLibrary.fMakeCurrent (mContext, mThebesSurface->Data(), - LOCAL_GL_UNSIGNED_BYTE, - mThebesSurface->Width(), - mThebesSurface->Height()); + = sOSMesaLibrary.fMakeCurrent(mContext, mThebesSurface->Data(), + LOCAL_GL_UNSIGNED_BYTE, + mThebesSurface->Width(), + mThebesSurface->Height()); NS_ASSERTION(succeeded, "Failed to make OSMesa context current!"); return succeeded; @@ -252,25 +262,27 @@ GLContextProviderOSMesa::CreateForWindow(nsIWidget *aWidget) } already_AddRefed -GLContextProviderOSMesa::CreateForNativePixmapSurface(gfxASurface *aSurface) -{ - return 0; -} - -already_AddRefed -GLContextProviderOSMesa::CreatePBuffer(const gfxIntSize &aSize, const ContextFormat& aFormat) +GLContextProviderOSMesa::CreateOffscreen(const gfxIntSize& aSize, + const ContextFormat& aFormat) { if (!sOSMesaLibrary.EnsureInitialized()) { return nsnull; } - nsRefPtr glContext = new GLContextOSMesa; + nsRefPtr glContext = new GLContextOSMesa(aFormat); - if (!glContext->Init(aSize, aFormat)) { + if (!glContext->Init(aSize)) + { return nsnull; } - return glContext.forget().get(); + return glContext.forget(); +} + +GLContext * +GLContextProviderOSMesa::GetGlobalContext() +{ + return nsnull; } } /* namespace gl */ diff --git a/gfx/thebes/GLContextProviderWGL.cpp b/gfx/thebes/GLContextProviderWGL.cpp index bf82798ab21..da20e78ca98 100644 --- a/gfx/thebes/GLContextProviderWGL.cpp +++ b/gfx/thebes/GLContextProviderWGL.cpp @@ -49,9 +49,62 @@ namespace gl { WGLLibrary sWGLLibrary; -static HWND gDummyWindow = 0; -static HDC gDummyWindowDC = 0; -static HANDLE gDummyWindowGLContext = 0; +static HWND gSharedWindow = 0; +static HDC gSharedWindowDC = 0; +static HGLRC gSharedWindowGLContext = 0; +static int gSharedWindowPixelFormat = 0; + +static HWND +CreateDummyWindow(HDC *aWindowDC = nsnull) +{ + WNDCLASSW wc; + if (!GetClassInfoW(GetModuleHandle(NULL), L"GLContextWGLClass", &wc)) { + ZeroMemory(&wc, sizeof(WNDCLASSW)); + wc.style = CS_OWNDC; + wc.hInstance = GetModuleHandle(NULL); + wc.lpfnWndProc = DefWindowProc; + wc.lpszClassName = L"GLContextWGLClass"; + if (!RegisterClassW(&wc)) { + NS_WARNING("Failed to register GLContextWGLClass?!"); + // er. failed to register our class? + return NULL; + } + } + + HWND win = CreateWindowW(L"GLContextWGLClass", L"GLContextWGL", 0, + 0, 0, 16, 16, + NULL, NULL, GetModuleHandle(NULL), NULL); + NS_ENSURE_TRUE(win, NULL); + + HDC dc = GetDC(win); + NS_ENSURE_TRUE(dc, NULL); + + if (gSharedWindowPixelFormat == 0) { + PIXELFORMATDESCRIPTOR pfd; + ZeroMemory(&pfd, sizeof(PIXELFORMATDESCRIPTOR)); + pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.cColorBits = 24; + pfd.cDepthBits = 0; + pfd.iLayerType = PFD_MAIN_PLANE; + + gSharedWindowPixelFormat = ChoosePixelFormat(dc, &pfd); + } + + if (!SetPixelFormat(dc, gSharedWindowPixelFormat, NULL)) { + NS_WARNING("SetPixelFormat failed!"); + DestroyWindow(win); + return NULL; + } + + if (aWindowDC) { + *aWindowDC = dc; + } + + return win; +} PRBool WGLLibrary::EnsureInitialized() @@ -74,6 +127,7 @@ WGLLibrary::EnsureInitialized() { (PRFuncPtr*) &fDeleteContext, { "wglDeleteContext", NULL } }, { (PRFuncPtr*) &fGetCurrentContext, { "wglGetCurrentContext", NULL } }, { (PRFuncPtr*) &fGetCurrentDC, { "wglGetCurrentDC", NULL } }, + { (PRFuncPtr*) &fShareLists, { "wglShareLists", NULL } }, { NULL, { NULL } } }; @@ -82,61 +136,19 @@ WGLLibrary::EnsureInitialized() return PR_FALSE; } - // This is ridiculous -- we have to actually create a context to get the OpenGL - // ICD to load. - - WNDCLASSW wc; - if (!GetClassInfoW(GetModuleHandle(NULL), L"DummyGLWindowClass", &wc)) { - ZeroMemory(&wc, sizeof(WNDCLASSW)); - wc.hInstance = GetModuleHandle(NULL); - wc.lpfnWndProc = DefWindowProc; - wc.lpszClassName = L"DummyGLWindowClass"; - if (!RegisterClassW(&wc)) { - NS_WARNING("Failed to register DummyGLWindowClass?!"); - // er. failed to register our class? - return PR_FALSE; - } - } - - gDummyWindow = CreateWindowW(L"DummyGLWindowClass", L"DummyGLWindow", 0, - CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, - CW_USEDEFAULT, NULL, NULL, GetModuleHandle(NULL), NULL); - if (!gDummyWindow) { - NS_WARNING("CreateWindow DummyGLWindow failed"); - return PR_FALSE; - } - - gDummyWindowDC = GetDC(gDummyWindow); - if (!gDummyWindowDC) { - NS_WARNING("GetDC gDummyWindow failed"); - return PR_FALSE; - } - - // find default pixel format - PIXELFORMATDESCRIPTOR pfd; - ZeroMemory(&pfd, sizeof(PIXELFORMATDESCRIPTOR)); - pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); - pfd.nVersion = 1; - pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL; - int pixelformat = ChoosePixelFormat(gDummyWindowDC, &pfd); - - // set the pixel format for the dc - if (!SetPixelFormat(gDummyWindowDC, pixelformat, &pfd)) { - NS_WARNING("SetPixelFormat failed"); - return PR_FALSE; - } + // This is ridiculous -- we have to actually create a context to + // get the OpenGL ICD to load. + gSharedWindow = CreateDummyWindow(&gSharedWindowDC); + NS_ENSURE_TRUE(gSharedWindow, PR_FALSE); // create rendering context - gDummyWindowGLContext = fCreateContext(gDummyWindowDC); - if (!gDummyWindowGLContext) { - NS_WARNING("wglCreateContext failed"); - return PR_FALSE; - } + gSharedWindowGLContext = fCreateContext(gSharedWindowDC); + NS_ENSURE_TRUE(gSharedWindowGLContext, PR_FALSE); HGLRC curCtx = fGetCurrentContext(); HDC curDC = fGetCurrentDC(); - if (!fMakeCurrent((HDC)gDummyWindowDC, (HGLRC)gDummyWindowGLContext)) { + if (!fMakeCurrent((HDC)gSharedWindowDC, (HGLRC)gSharedWindowGLContext)) { NS_WARNING("wglMakeCurrent failed"); return PR_FALSE; } @@ -177,33 +189,75 @@ WGLLibrary::EnsureInitialized() fMakeCurrent(curDC, curCtx); mInitialized = PR_TRUE; + + // Call this to create the global GLContext instance, + // and to check for errors. Note that this must happen /after/ + // setting mInitialized to TRUE, or an infinite loop results. + if (GLContextProviderWGL::GetGlobalContext() == nsnull) { + mInitialized = PR_FALSE; + return PR_FALSE; + } + return PR_TRUE; } class GLContextWGL : public GLContext { public: - GLContextWGL(HDC aDC, HGLRC aContext) - : mContext(aContext), mDC(aDC), mPBuffer(nsnull), mPixelFormat(-1) - { } + GLContextWGL(const ContextFormat& aFormat, + GLContext *aSharedContext, + HDC aDC, + HGLRC aContext, + HWND aWindow = nsnull, + PRBool aIsOffscreen = PR_FALSE) + : GLContext(aFormat, aIsOffscreen, aSharedContext), + mDC(aDC), + mContext(aContext), + mWnd(aWindow), + mPBuffer(NULL), + mPixelFormat(0) + { + } - GLContextWGL(HANDLE aPBuffer, int aPixelFormat) { - mPBuffer = aPBuffer; - mPixelFormat = aPixelFormat; - mDC = sWGLLibrary.fGetPbufferDC(mPBuffer); - mContext = sWGLLibrary.fCreateContext(mDC); + GLContextWGL(const ContextFormat& aFormat, + GLContext *aSharedContext, + HANDLE aPbuffer, + HDC aDC, + HGLRC aContext, + int aPixelFormat) + : GLContext(aFormat, PR_TRUE, aSharedContext), + mDC(aDC), + mContext(aContext), + mWnd(NULL), + mPBuffer(aPbuffer), + mPixelFormat(aPixelFormat) + { } ~GLContextWGL() { + if (mOffscreenFBO) { + MakeCurrent(); + DeleteOffscreenFBO(); + } + sWGLLibrary.fDeleteContext(mContext); if (mPBuffer) sWGLLibrary.fDestroyPbuffer(mPBuffer); + if (mWnd) + DestroyWindow(mWnd); + } + + GLContextType GetContextType() { + return ContextTypeWGL; } PRBool Init() { + if (!mDC || !mContext) + return PR_FALSE; + MakeCurrent(); SetupLookupFunction(); return InitWithPrefix("gl", PR_TRUE); @@ -237,34 +291,100 @@ public: case NativeGLContext: return mContext; - case NativePBuffer: - return mPBuffer; - default: return nsnull; } } - PRBool Resize(const gfxIntSize& aNewSize) { - if (!mPBuffer) + PRBool BindTex2DOffscreen(GLContext *aOffscreen); + void UnbindTex2DOffscreen(GLContext *aOffscreen); + PRBool ResizeOffscreen(const gfxIntSize& aNewSize); + + HGLRC Context() { return mContext; } + + virtual already_AddRefed + CreateBasicTextureImage(GLuint aTexture, + const nsIntSize& aSize, + TextureImage::ContentType aContentType, + GLContext* aContext); + +protected: + friend class GLContextProviderWGL; + + HDC mDC; + HGLRC mContext; + HWND mWnd; + HANDLE mPBuffer; + int mPixelFormat; +}; + +PRBool +GLContextWGL::BindTex2DOffscreen(GLContext *aOffscreen) +{ + if (aOffscreen->GetContextType() != ContextTypeWGL) { + NS_WARNING("non-WGL context"); + return PR_FALSE; + } + + if (!aOffscreen->IsOffscreen()) { + NS_WARNING("non-offscreen context"); + return PR_FALSE; + } + + GLContextWGL *offs = static_cast(aOffscreen); + + if (offs->mPBuffer) { + BOOL ok = sWGLLibrary.fBindTexImage(offs->mPBuffer, + LOCAL_WGL_FRONT_LEFT_ARB); + if (!ok) { + NS_WARNING("CanvasLayerOGL::Updated wglBindTexImageARB failed"); + return PR_FALSE; + } + } else if (offs->mOffscreenTexture) { + if (offs->GetSharedContext() != GLContextProviderWGL::GetGlobalContext()) + { + NS_WARNING("offscreen FBO context can only be bound with context sharing!"); return PR_FALSE; - - nsTArray pbattribs; - pbattribs.AppendElement(LOCAL_WGL_TEXTURE_FORMAT_ARB); - // XXX fixme after bug 571092 lands and we have the format available - if (true /*aFormat.alpha > 0*/) { - pbattribs.AppendElement(LOCAL_WGL_TEXTURE_RGBA_ARB); - } else { - pbattribs.AppendElement(LOCAL_WGL_TEXTURE_RGB_ARB); } - pbattribs.AppendElement(LOCAL_WGL_TEXTURE_TARGET_ARB); - pbattribs.AppendElement(LOCAL_WGL_TEXTURE_2D_ARB); - pbattribs.AppendElement(0); + fBindTexture(LOCAL_GL_TEXTURE_2D, offs->mOffscreenTexture); + } else { + NS_WARNING("don't know how to bind this!"); + return PR_FALSE; + } - HANDLE newbuf = sWGLLibrary.fCreatePbuffer(gDummyWindowDC, mPixelFormat, + return PR_TRUE; +} + +void +GLContextWGL::UnbindTex2DOffscreen(GLContext *aOffscreen) +{ + NS_ASSERTION(aOffscreen->GetContextType() == ContextTypeWGL, "wrong type"); + + GLContextWGL *offs = static_cast(aOffscreen); + if (offs->mPBuffer) { + // XXX so, according to the extension, ReleaseTexImage is not required to + // preserve color buffer contents. This sucks, but everywhere that I've + // tried it the color buffer is preserved. So let's cross our fingers.. + sWGLLibrary.fReleaseTexImage(offs->mPBuffer, LOCAL_WGL_FRONT_LEFT_ARB); + } +} + +PRBool +GLContextWGL::ResizeOffscreen(const gfxIntSize& aNewSize) +{ + if (mPBuffer) { + int pbattrs[] = { + LOCAL_WGL_TEXTURE_FORMAT_ARB, + mCreationFormat.alpha > 0 ? LOCAL_WGL_TEXTURE_RGBA_ARB + : LOCAL_WGL_TEXTURE_RGB_ARB, + LOCAL_WGL_TEXTURE_TARGET_ARB, LOCAL_WGL_TEXTURE_2D_ARB, + 0 + }; + + HANDLE newbuf = sWGLLibrary.fCreatePbuffer(gSharedWindowDC, mPixelFormat, aNewSize.width, aNewSize.height, - pbattribs.Elements()); + pbattrs); if (!newbuf) return PR_FALSE; @@ -274,30 +394,28 @@ public: isCurrent = true; } - // hey, it worked! sWGLLibrary.fDestroyPbuffer(mPBuffer); mPBuffer = newbuf; mDC = sWGLLibrary.fGetPbufferDC(mPBuffer); - if (isCurrent) - MakeCurrent(); + mOffscreenSize = aNewSize; + mOffscreenActualSize = aNewSize; + + MakeCurrent(); + ClearSafely(); return PR_TRUE; } - virtual already_AddRefed - CreateBasicTextureImage(GLuint aTexture, - const nsIntSize& aSize, - TextureImage::ContentType aContentType, - GLContext* aContext); + return ResizeOffscreenFBO(aNewSize); +} -private: - HGLRC mContext; - HDC mDC; - HANDLE mPBuffer; - int mPixelFormat; -}; +static GLContextWGL * +GetGlobalContextWGL() +{ + return static_cast(GLContextProviderWGL::GetGlobalContext()); +} class TextureImageWGL : public BasicTextureImage { @@ -351,6 +469,7 @@ GLContextProviderWGL::CreateForWindow(nsIWidget *aWidget) if (!sWGLLibrary.EnsureInitialized()) { return nsnull; } + /** * We need to make sure we call SetPixelFormat -after- calling * EnsureInitialized, otherwise it can load/unload the dll and @@ -359,75 +478,77 @@ GLContextProviderWGL::CreateForWindow(nsIWidget *aWidget) HDC dc = (HDC)aWidget->GetNativeData(NS_NATIVE_GRAPHIC); - PIXELFORMATDESCRIPTOR pfd; - ZeroMemory(&pfd, sizeof(pfd)); - - pfd.nSize = sizeof(pfd); - pfd.nVersion = 1; - pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL; - pfd.iPixelType = PFD_TYPE_RGBA; - pfd.cColorBits = 32; - pfd.cDepthBits = 0; - pfd.iLayerType = PFD_MAIN_PLANE; - int iFormat = ChoosePixelFormat(dc, &pfd); - - SetPixelFormat(dc, iFormat, &pfd); + SetPixelFormat(dc, gSharedWindowPixelFormat, NULL); HGLRC context = sWGLLibrary.fCreateContext(dc); if (!context) { return nsnull; } - nsRefPtr glContext = new GLContextWGL(dc, context); - glContext->Init(); + GLContextWGL *shareContext = GetGlobalContextWGL(); + if (shareContext && + !sWGLLibrary.fShareLists(shareContext->Context(), context)) + { + shareContext = nsnull; + } - return glContext.forget().get(); -} - -already_AddRefed -GLContextProviderWGL::CreatePBuffer(const gfxIntSize& aSize, const ContextFormat& aFormat) -{ - if (!sWGLLibrary.EnsureInitialized()) { + nsRefPtr glContext = new GLContextWGL(ContextFormat(ContextFormat::BasicRGB24), + shareContext, dc, context); + if (!glContext->Init()) { return nsnull; } - nsTArray attribs; + return glContext.forget(); +} -#define A1_(_x) do { attribs.AppendElement(_x); } while(0) -#define A2_(_x,_y) do { \ - attribs.AppendElement(_x); \ - attribs.AppendElement(_y); \ - } while(0) +static already_AddRefed +CreatePBufferOffscreenContext(const gfxIntSize& aSize, + const ContextFormat& aFormat) +{ +#define A1(_a,_x) do { _a.AppendElement(_x); } while(0) +#define A2(_a,_x,_y) do { _a.AppendElement(_x); _a.AppendElement(_y); } while(0) - A2_(LOCAL_WGL_SUPPORT_OPENGL_ARB, LOCAL_GL_TRUE); - A2_(LOCAL_WGL_DRAW_TO_PBUFFER_ARB, LOCAL_GL_TRUE); - A2_(LOCAL_WGL_DOUBLE_BUFFER_ARB, LOCAL_GL_FALSE); + nsTArray attrs; - A2_(LOCAL_WGL_ACCELERATION_ARB, LOCAL_WGL_FULL_ACCELERATION_ARB); + A2(attrs, LOCAL_WGL_SUPPORT_OPENGL_ARB, LOCAL_GL_TRUE); + A2(attrs, LOCAL_WGL_DRAW_TO_PBUFFER_ARB, LOCAL_GL_TRUE); + A2(attrs, LOCAL_WGL_DOUBLE_BUFFER_ARB, LOCAL_GL_FALSE); - A2_(LOCAL_WGL_COLOR_BITS_ARB, aFormat.colorBits()); - A2_(LOCAL_WGL_RED_BITS_ARB, aFormat.red); - A2_(LOCAL_WGL_GREEN_BITS_ARB, aFormat.green); - A2_(LOCAL_WGL_BLUE_BITS_ARB, aFormat.blue); - A2_(LOCAL_WGL_ALPHA_BITS_ARB, aFormat.alpha); + A2(attrs, LOCAL_WGL_ACCELERATION_ARB, LOCAL_WGL_FULL_ACCELERATION_ARB); - A2_(LOCAL_WGL_DEPTH_BITS_ARB, aFormat.depth); + A2(attrs, LOCAL_WGL_COLOR_BITS_ARB, aFormat.colorBits()); + A2(attrs, LOCAL_WGL_RED_BITS_ARB, aFormat.red); + A2(attrs, LOCAL_WGL_GREEN_BITS_ARB, aFormat.green); + A2(attrs, LOCAL_WGL_BLUE_BITS_ARB, aFormat.blue); + A2(attrs, LOCAL_WGL_ALPHA_BITS_ARB, aFormat.alpha); - if (aFormat.alpha > 0) - A2_(LOCAL_WGL_BIND_TO_TEXTURE_RGBA_ARB, LOCAL_GL_TRUE); - else - A2_(LOCAL_WGL_BIND_TO_TEXTURE_RGB_ARB, LOCAL_GL_TRUE); + A2(attrs, LOCAL_WGL_DEPTH_BITS_ARB, aFormat.depth); - A2_(LOCAL_WGL_DOUBLE_BUFFER_ARB, LOCAL_GL_FALSE); - A2_(LOCAL_WGL_STEREO_ARB, LOCAL_GL_FALSE); + if (aFormat.alpha > 0) { + A2(attrs, LOCAL_WGL_BIND_TO_TEXTURE_RGBA_ARB, LOCAL_GL_TRUE); + } else { + A2(attrs, LOCAL_WGL_BIND_TO_TEXTURE_RGB_ARB, LOCAL_GL_TRUE); + } - A1_(0); + A2(attrs, LOCAL_WGL_DOUBLE_BUFFER_ARB, LOCAL_GL_FALSE); + A2(attrs, LOCAL_WGL_STEREO_ARB, LOCAL_GL_FALSE); -#define MAX_NUM_FORMATS 256 - UINT numFormats = MAX_NUM_FORMATS; - int formats[MAX_NUM_FORMATS]; + A1(attrs, 0); - if (!sWGLLibrary.fChoosePixelFormat(gDummyWindowDC, - attribs.Elements(), NULL, + nsTArray pbattrs; + A2(pbattrs, LOCAL_WGL_TEXTURE_TARGET_ARB, LOCAL_WGL_TEXTURE_2D_ARB); + + if (aFormat.alpha > 0) { + A2(pbattrs, LOCAL_WGL_TEXTURE_FORMAT_ARB, LOCAL_WGL_TEXTURE_RGBA_ARB); + } else { + A2(pbattrs, LOCAL_WGL_TEXTURE_FORMAT_ARB, LOCAL_WGL_TEXTURE_RGB_ARB); + } + A1(pbattrs, 0); + + UINT numFormats = 256; + int formats[256]; + + if (!sWGLLibrary.fChoosePixelFormat(gSharedWindowDC, + attrs.Elements(), NULL, numFormats, formats, &numFormats) || numFormats == 0) { @@ -437,33 +558,106 @@ GLContextProviderWGL::CreatePBuffer(const gfxIntSize& aSize, const ContextFormat // XXX add back the priority choosing code here int chosenFormat = formats[0]; - nsTArray pbattribs; - pbattribs.AppendElement(LOCAL_WGL_TEXTURE_FORMAT_ARB); - if (aFormat.alpha > 0) { - pbattribs.AppendElement(LOCAL_WGL_TEXTURE_RGBA_ARB); - } else { - pbattribs.AppendElement(LOCAL_WGL_TEXTURE_RGB_ARB); - } - pbattribs.AppendElement(LOCAL_WGL_TEXTURE_TARGET_ARB); - pbattribs.AppendElement(LOCAL_WGL_TEXTURE_2D_ARB); - - // hmm, do we need mipmaps? - //pbattribs.AppendElement(LOCAL_WGL_MIPMAP_TEXTURE_ARB); - //pbattribs.AppendElement(LOCAL_GL_TRUE); - - pbattribs.AppendElement(0); - - HANDLE pbuffer = sWGLLibrary.fCreatePbuffer(gDummyWindowDC, chosenFormat, + HANDLE pbuffer = sWGLLibrary.fCreatePbuffer(gSharedWindowDC, chosenFormat, aSize.width, aSize.height, - pbattribs.Elements()); + pbattrs.Elements()); if (!pbuffer) { return nsnull; } - nsRefPtr glContext = new GLContextWGL(pbuffer, chosenFormat); - glContext->Init(); + HDC pbdc = sWGLLibrary.fGetPbufferDC(pbuffer); + NS_ASSERTION(pbdc, "expected a dc"); - return glContext.forget().get(); + HGLRC context = sWGLLibrary.fCreateContext(pbdc); + if (!context) { + sWGLLibrary.fDestroyPbuffer(pbuffer); + return PR_FALSE; + } + + nsRefPtr glContext = new GLContextWGL(aFormat, + nsnull, + pbuffer, + pbdc, + context, + chosenFormat); + + return glContext.forget(); +} + +static already_AddRefed +CreateWindowOffscreenContext(const gfxIntSize& aSize, + const ContextFormat& aFormat) +{ + // CreateWindowOffscreenContext must return a global-shared context + GLContextWGL *shareContext = GetGlobalContextWGL(); + if (!shareContext) { + return nsnull; + } + + HDC dc; + HWND win = CreateDummyWindow(&dc); + if (!win) { + return nsnull; + } + + HGLRC context = sWGLLibrary.fCreateContext(dc); + if (!context) { + return nsnull; + } + + if (!sWGLLibrary.fShareLists(shareContext->Context(), context)) { + NS_WARNING("wglShareLists failed!"); + + sWGLLibrary.fDeleteContext(context); + DestroyWindow(win); + return nsnull; + } + + nsRefPtr glContext = new GLContextWGL(aFormat, shareContext, + dc, context, win, PR_TRUE); + + return glContext.forget(); +} + +already_AddRefed +GLContextProviderWGL::CreateOffscreen(const gfxIntSize& aSize, + const ContextFormat& aFormat) +{ + if (!sWGLLibrary.EnsureInitialized()) { + return nsnull; + } + + nsRefPtr glContext; + + // Always try to create a pbuffer context first, because we + // want the context isolation. + if (sWGLLibrary.fCreatePbuffer && + sWGLLibrary.fChoosePixelFormat) + { + glContext = CreatePBufferOffscreenContext(aSize, aFormat); + } + + // If it failed, then create a window context and use a FBO. + if (!glContext) { + glContext = CreateWindowOffscreenContext(aSize, aFormat); + } + + if (!glContext || + !glContext->Init()) + { + return nsnull; + } + + glContext->mOffscreenSize = aSize; + glContext->mOffscreenActualSize = aSize; + + if (!glContext->mPBuffer && + !glContext->ResizeOffscreenFBO(aSize)) + { + return nsnull; + } + + return glContext.forget(); } already_AddRefed @@ -472,5 +666,32 @@ GLContextProviderWGL::CreateForNativePixmapSurface(gfxASurface *aSurface) return nsnull; } +static nsRefPtr gGlobalContext; + +GLContext * +GLContextProviderWGL::GetGlobalContext() +{ + if (!sWGLLibrary.EnsureInitialized()) { + return nsnull; + } + + static bool triedToCreateContext = false; + + if (!triedToCreateContext && !gGlobalContext) { + triedToCreateContext = true; + + // conveniently, we already have what we need... + gGlobalContext = new GLContextWGL(ContextFormat(ContextFormat::BasicRGB24), nsnull, + gSharedWindowDC, gSharedWindowGLContext); + if (!gGlobalContext->Init()) { + NS_WARNING("Global context GLContext initialization failed?"); + gGlobalContext = nsnull; + return PR_FALSE; + } + } + + return static_cast(gGlobalContext); +} + } /* namespace gl */ } /* namespace mozilla */ diff --git a/gfx/thebes/GLXLibrary.h b/gfx/thebes/GLXLibrary.h index b9fe80ad74c..d8fa23b38bc 100644 --- a/gfx/thebes/GLXLibrary.h +++ b/gfx/thebes/GLXLibrary.h @@ -100,6 +100,23 @@ public: int); PFNGLXQUERYSERVERSTRING xQueryServerString; + typedef GLXPixmap (GLAPIENTRY * PFNGLXCREATEPIXMAP) (Display *, + GLXFBConfig, + Pixmap, + const int *); + PFNGLXCREATEPIXMAP xCreatePixmap; + typedef void (GLAPIENTRY * PFNGLXDESTROYPIXMAP) (Display *, + GLXPixmap); + PFNGLXDESTROYPIXMAP xDestroyPixmap; + typedef const char * (GLAPIENTRY * PFNGLXGETCLIENTSTRING) (Display *, + int); + PFNGLXGETCLIENTSTRING xGetClientString; + typedef GLXContext (GLAPIENTRY * PFNGLXCREATECONTEXT) (Display *, + XVisualInfo *, + GLXContext, + Bool); + PFNGLXCREATECONTEXT xCreateContext; + PRBool EnsureInitialized(); private: diff --git a/gfx/thebes/WGLLibrary.h b/gfx/thebes/WGLLibrary.h index e8df645c136..6f7065151f3 100644 --- a/gfx/thebes/WGLLibrary.h +++ b/gfx/thebes/WGLLibrary.h @@ -56,6 +56,8 @@ public: PFNWGLGETCURRENTCONTEXT fGetCurrentContext; typedef HDC (GLAPIENTRY * PFNWGLGETCURRENTDC) (void); PFNWGLGETCURRENTDC fGetCurrentDC; + typedef BOOL (GLAPIENTRY * PFNWGLSHARELISTS) (HGLRC oldContext, HGLRC newContext); + PFNWGLSHARELISTS fShareLists; typedef HANDLE (WINAPI * PFNWGLCREATEPBUFFERPROC) (HDC hDC, int iPixelFormat, int iWidth, int iHeight, const int* piAttribList); PFNWGLCREATEPBUFFERPROC fCreatePbuffer;