From 95c827946fde5f4f167dad45c4d6f28aaa8624ff Mon Sep 17 00:00:00 2001 From: Vladimir Vukicevic Date: Sun, 18 Jul 2010 22:01:14 -0700 Subject: [PATCH] b=571831; use the new CreateOffscreen API in WebGL; r=bjacob --- content/canvas/src/WebGLContext.cpp | 185 +++++++++++++++----- content/canvas/src/WebGLContext.h | 3 +- content/canvas/src/WebGLContextGL.cpp | 87 +++++++-- content/canvas/src/WebGLContextValidate.cpp | 4 +- 4 files changed, 219 insertions(+), 60 deletions(-) diff --git a/content/canvas/src/WebGLContext.cpp b/content/canvas/src/WebGLContext.cpp index 421d79e2449..21a56c95ec0 100644 --- a/content/canvas/src/WebGLContext.cpp +++ b/content/canvas/src/WebGLContext.cpp @@ -99,6 +99,106 @@ WebGLContext::WebGLContext() WebGLContext::~WebGLContext() { + DestroyResourcesAndContext(); +} + +static PLDHashOperator +DeleteTextureFunction(const PRUint32& aKey, WebGLTexture *aValue, void *aData) +{ + gl::GLContext *gl = (gl::GLContext *) aData; + NS_ASSERTION(!aValue->Deleted(), "Texture is still in mMapTextures, but is deleted?"); + GLuint name = aValue->GLName(); + gl->fDeleteTextures(1, &name); + aValue->Delete(); + return PL_DHASH_NEXT; +} + +static PLDHashOperator +DeleteBufferFunction(const PRUint32& aKey, WebGLBuffer *aValue, void *aData) +{ + gl::GLContext *gl = (gl::GLContext *) aData; + NS_ASSERTION(!aValue->Deleted(), "Buffer is still in mMapBuffers, but is deleted?"); + GLuint name = aValue->GLName(); + gl->fDeleteBuffers(1, &name); + aValue->Delete(); + return PL_DHASH_NEXT; +} + +static PLDHashOperator +DeleteFramebufferFunction(const PRUint32& aKey, WebGLFramebuffer *aValue, void *aData) +{ + gl::GLContext *gl = (gl::GLContext *) aData; + NS_ASSERTION(!aValue->Deleted(), "Framebuffer is still in mMapFramebuffers, but is deleted?"); + GLuint name = aValue->GLName(); + gl->fDeleteFramebuffers(1, &name); + aValue->Delete(); + return PL_DHASH_NEXT; +} + +static PLDHashOperator +DeleteRenderbufferFunction(const PRUint32& aKey, WebGLRenderbuffer *aValue, void *aData) +{ + gl::GLContext *gl = (gl::GLContext *) aData; + NS_ASSERTION(!aValue->Deleted(), "Renderbuffer is still in mMapRenderbuffers, but is deleted?"); + GLuint name = aValue->GLName(); + gl->fDeleteRenderbuffers(1, &name); + aValue->Delete(); + return PL_DHASH_NEXT; +} + +static PLDHashOperator +DeleteProgramFunction(const PRUint32& aKey, WebGLProgram *aValue, void *aData) +{ + gl::GLContext *gl = (gl::GLContext *) aData; + NS_ASSERTION(!aValue->Deleted(), "Program is still in mMapPrograms, but is deleted?"); + GLuint name = aValue->GLName(); + gl->fDeleteProgram(name); + aValue->Delete(); + return PL_DHASH_NEXT; +} + +static PLDHashOperator +DeleteShaderFunction(const PRUint32& aKey, WebGLShader *aValue, void *aData) +{ + gl::GLContext *gl = (gl::GLContext *) aData; + NS_ASSERTION(!aValue->Deleted(), "Shader is still in mMapShaders, but is deleted?"); + GLuint name = aValue->GLName(); + gl->fDeleteShader(name); + aValue->Delete(); + return PL_DHASH_NEXT; +} + +void +WebGLContext::DestroyResourcesAndContext() +{ + if (!gl) + return; + + gl->MakeCurrent(); + + mMapTextures.EnumerateRead(DeleteTextureFunction, gl); + mMapTextures.Clear(); + + mMapBuffers.EnumerateRead(DeleteBufferFunction, gl); + mMapBuffers.Clear(); + + mMapPrograms.EnumerateRead(DeleteProgramFunction, gl); + mMapPrograms.Clear(); + + mMapShaders.EnumerateRead(DeleteShaderFunction, gl); + mMapShaders.Clear(); + + mMapFramebuffers.EnumerateRead(DeleteFramebufferFunction, gl); + mMapFramebuffers.Clear(); + + mMapRenderbuffers.EnumerateRead(DeleteRenderbufferFunction, gl); + mMapRenderbuffers.Clear(); + + // We just got rid of everything, so the context had better + // have been going away. + printf_stderr("--- WebGL context destroyed: %p\n", gl.get()); + + gl = nsnull; } void @@ -141,38 +241,42 @@ WebGLContext::SetCanvasElement(nsHTMLCanvasElement* aParentCanvas) NS_IMETHODIMP WebGLContext::SetDimensions(PRInt32 width, PRInt32 height) { + if (mWidth == width && mHeight == height) + return NS_OK; + + // If we already have a gl context, then we just need to resize + // FB0. + if (gl && + gl->ResizeOffscreen(gfxIntSize(width, height))) + { + // everything's good, we're done here + mWidth = width; + mHeight = height; + return NS_OK; + } + + // We're going to create an entirely new context. If our + // generation is not 0 right now (that is, if this isn't the first + // context we're creating), we may have to dispatch a context lost + // event. + // If incrementing the generation would cause overflow, // don't allow it. Allowing this would allow us to use // resource handles created from older context generations. if (!(mGeneration+1).valid()) return NS_ERROR_FAILURE; // exit without changing the value of mGeneration - if (mWidth == width && mHeight == height) - return NS_OK; - - if (gl) { - // hey we already have something - if (gl->Resize(gfxIntSize(width, height))) { - - mWidth = width; - mHeight = height; - - gl->fViewport(0, 0, mWidth, mHeight); - gl->fClearColor(0, 0, 0, 0); - gl->fClear(LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT | LOCAL_GL_STENCIL_BUFFER_BIT); - - // great success! - return NS_OK; - } - } + // We're going to recreate our context, so make sure we clean up + // after ourselves. + DestroyResourcesAndContext(); gl::ContextFormat format(gl::ContextFormat::BasicRGBA32); format.depth = 16; format.minDepth = 1; - gl = gl::GLContextProvider::CreatePBuffer(gfxIntSize(width, height), - gl::GLContextProvider::GetGlobalContext(), - format); + gl = gl::GLContextProvider::CreateOffscreen(gfxIntSize(width, height), format); + + printf_stderr ("--- WebGL context created: %p\n", gl.get()); #ifdef USE_GLES2 // On native GLES2, no need to validate, the compiler will do it @@ -186,16 +290,13 @@ WebGLContext::SetDimensions(PRInt32 width, PRInt32 height) #endif if (!InitAndValidateGL()) { - gl = gl::GLContextProviderOSMesa::CreatePBuffer(gfxIntSize(width, height), - nsnull, - format); + gl = gl::GLContextProviderOSMesa::CreateOffscreen(gfxIntSize(width, height), format); if (!InitAndValidateGL()) { LogMessage("WebGL: Can't get a usable OpenGL context."); return NS_ERROR_FAILURE; } - else { - LogMessage("WebGL: Using software rendering via OSMesa"); - } + + LogMessage("WebGL: Using software rendering via OSMesa"); } mWidth = width; @@ -205,12 +306,25 @@ WebGLContext::SetDimensions(PRInt32 width, PRInt32 height) // increment the generation number ++mGeneration; +#if 0 + if (mGeneration > 0) { + // XXX dispatch context lost event + } +#endif + MakeContextCurrent(); // Make sure that we clear this out, otherwise // we'll end up displaying random memory + gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, gl->GetOffscreenFBO()); gl->fViewport(0, 0, mWidth, mHeight); - gl->fClearColor(0, 0, 0, 0); + gl->fClearColor(0.0f, 0.0f, 0.0f, 0.0f); +#ifdef USE_GLES2 + gl->fClearDepthf(0.0f); +#else + gl->fClearDepth(0.0f); +#endif + gl->fClearStencil(0); gl->fClear(LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT | LOCAL_GL_STENCIL_BUFFER_BIT); return NS_OK; @@ -348,26 +462,19 @@ WebGLContext::GetCanvasLayer(CanvasLayer *aOldLayer, // data with the gl context directly, or may provide a surface to which it renders (this is the case // of OSMesa contexts), in which case we want to initialize data with that surface. - void* native_pbuffer = gl->GetNativeData(gl::GLContext::NativePBuffer); void* native_surface = gl->GetNativeData(gl::GLContext::NativeImageSurface); - if (native_pbuffer) { - data.mGLContext = gl.get(); - } - else if (native_surface) { + if (native_surface) { data.mSurface = static_cast(native_surface); - } - else { - NS_WARNING("The GLContext has neither a native PBuffer nor a native surface!"); - return nsnull; + } else { + data.mGLContext = gl.get(); } data.mSize = nsIntSize(mWidth, mHeight); data.mGLBufferIsPremultiplied = PR_FALSE; canvasLayer->Initialize(data); - // once we support GL context attributes, we'll set the right thing here - canvasLayer->SetIsOpaqueContent(PR_FALSE); + canvasLayer->SetIsOpaqueContent(gl->CreationFormat().alpha == 0 ? PR_TRUE : PR_FALSE); canvasLayer->Updated(nsIntRect(0, 0, mWidth, mHeight)); mInvalidated = PR_FALSE; diff --git a/content/canvas/src/WebGLContext.h b/content/canvas/src/WebGLContext.h index df3f210e379..0b27fa1bfc8 100644 --- a/content/canvas/src/WebGLContext.h +++ b/content/canvas/src/WebGLContext.h @@ -57,7 +57,7 @@ #include "nsIDOMHTMLElement.h" #include "nsIJSNativeInitializer.h" -#include "GLContext.h" +#include "GLContextProvider.h" #include "Layers.h" #include "CheckedInt.h" @@ -359,6 +359,7 @@ protected: PRBool ValidateDrawModeEnum(WebGLenum mode, const char *info); void Invalidate(); + void DestroyResourcesAndContext(); void MakeContextCurrent() { gl->MakeCurrent(); } diff --git a/content/canvas/src/WebGLContextGL.cpp b/content/canvas/src/WebGLContextGL.cpp index 7db382baec5..4ab749b6c8e 100644 --- a/content/canvas/src/WebGLContextGL.cpp +++ b/content/canvas/src/WebGLContextGL.cpp @@ -227,7 +227,11 @@ WebGLContext::BindFramebuffer(WebGLenum target, nsIWebGLFramebuffer *fbobj) MakeContextCurrent(); - gl->fBindFramebuffer(target, framebuffername); + if (isNull) { + gl->fBindFramebuffer(target, gl->GetOffscreenFBO()); + } else { + gl->fBindFramebuffer(target, framebuffername); + } mBoundFramebuffer = wfb; @@ -503,7 +507,9 @@ NS_IMETHODIMP WebGLContext::CheckFramebufferStatus(WebGLenum target, WebGLenum *retval) { MakeContextCurrent(); - // XXX check target + if (target != LOCAL_GL_FRAMEBUFFER) + return ErrorInvalidEnum("CheckFramebufferStatus: target must be FRAMEBUFFER"); + *retval = gl->fCheckFramebufferStatus(target); return NS_OK; } @@ -1013,8 +1019,11 @@ WebGLContext::FramebufferRenderbuffer(WebGLenum target, WebGLenum attachment, We if (rbtarget != LOCAL_GL_RENDERBUFFER) return ErrorInvalidEnumInfo("framebufferRenderbuffer: renderbuffer target:", rbtarget); + if (!mBoundFramebuffer) + return ErrorInvalidOperation("FramebufferRenderbuffer: cannot modify framebuffer 0"); + // dimensions are kept for readPixels primarily, function only uses COLOR_ATTACHMENT0 - if (mBoundFramebuffer && attachment == LOCAL_GL_COLOR_ATTACHMENT0) + if (attachment == LOCAL_GL_COLOR_ATTACHMENT0) mBoundFramebuffer->setDimensions(wrb); MakeContextCurrent(); @@ -1055,8 +1064,11 @@ WebGLContext::FramebufferTexture2D(WebGLenum target, if (level != 0) return ErrorInvalidValue("FramebufferTexture2D: level must be 0"); + if (!mBoundFramebuffer) + return ErrorInvalidOperation("FramebufferTexture2D: cannot modify framebuffer 0"); + // dimensions are kept for readPixels primarily, function only uses COLOR_ATTACHMENT0 - if (mBoundFramebuffer && attachment == LOCAL_GL_COLOR_ATTACHMENT0) + if (attachment == LOCAL_GL_COLOR_ATTACHMENT0) mBoundFramebuffer->setDimensions(wtex); // XXXXX we need to store/reference this attachment! @@ -1478,7 +1490,7 @@ WebGLContext::GetParameter(PRUint32 pname, nsIVariant **retval) break; case LOCAL_GL_TEXTURE_BINDING_2D: - wrval->SetAsISupports( mBound2DTextures[mActiveTexture]); + wrval->SetAsISupports(mBound2DTextures[mActiveTexture]); break; case LOCAL_GL_TEXTURE_BINDING_CUBE_MAP: @@ -1544,22 +1556,61 @@ WebGLContext::GetFramebufferAttachmentParameter(WebGLenum target, WebGLenum atta return ErrorInvalidEnumInfo("GetFramebufferAttachmentParameter: attachment", attachment); } + if (!mBoundFramebuffer) + return ErrorInvalidOperation("GetFramebufferAttachmentParameter: cannot query framebuffer 0"); + MakeContextCurrent(); - switch (pname) { - case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: - case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: - case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL: - case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE: - { - GLint i = 0; - gl->fGetFramebufferAttachmentParameteriv(target, attachment, pname, &i); - wrval->SetAsInt32(i); - } - break; + GLint atype = 0; + gl->fGetFramebufferAttachmentParameteriv(target, attachment, LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &atype); - default: - return ErrorInvalidEnumInfo("GetFramebufferAttachmentParameter: parameter", pname); + if (atype == LOCAL_GL_RENDERBUFFER) { + switch (pname) { + case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: + wrval->SetAsInt32(atype); + break; + + case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: { + GLint i = 0; + gl->fGetFramebufferAttachmentParameteriv(target, attachment, pname, &i); + WebGLRenderbuffer *rb = mMapRenderbuffers.GetWeak(PRUint32(i)); + NS_ASSERTION(rb, "Expected to find renderbuffer in table, but it's not there?"); + wrval->SetAsISupports(rb); + } + break; + + default: + return ErrorInvalidEnum("GetFramebufferAttachmentParameter: invalid parameter"); + } + } else if (atype == LOCAL_GL_TEXTURE) { + switch (pname) { + case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: + wrval->SetAsInt32(atype); + break; + + case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: { + GLint i = 0; + gl->fGetFramebufferAttachmentParameteriv(target, attachment, pname, &i); + WebGLTexture *tex = mMapTextures.GetWeak(PRUint32(i)); + NS_ASSERTION(tex, "Expected to find texture in table, but it's not there?"); + wrval->SetAsISupports(tex); + } + break; + + case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL: + case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE: { + GLint i = 0; + gl->fGetFramebufferAttachmentParameteriv(target, attachment, pname, &i); + wrval->SetAsInt32(i); + } + break; + + default: + return ErrorInvalidEnum("GetFramebufferAttachmentParameter: invalid parameter"); + } + } else { + NS_WARNING("Unknown framebuffer attachment type?"); + return NS_ERROR_FAILURE; } *retval = wrval.forget().get(); diff --git a/content/canvas/src/WebGLContextValidate.cpp b/content/canvas/src/WebGLContextValidate.cpp index a346c8d98c4..5ed4e174347 100644 --- a/content/canvas/src/WebGLContextValidate.cpp +++ b/content/canvas/src/WebGLContextValidate.cpp @@ -384,7 +384,7 @@ WebGLContext::InitAndValidateGL() gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_ATTRIBS, (GLint*) &mGLMaxVertexAttribs); if (mGLMaxVertexAttribs < 8) { - LogMessage("GL_MAX_VERTEX_ATTRIBS is < 8!"); + LogMessage("GL_MAX_VERTEX_ATTRIBS: %d is < 8!", mGLMaxVertexAttribs); return PR_FALSE; } @@ -395,7 +395,7 @@ WebGLContext::InitAndValidateGL() // GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS value is the accurate value. gl->fGetIntegerv(LOCAL_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, (GLint*) &mGLMaxTextureUnits); if (mGLMaxTextureUnits < 8) { - LogMessage("GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS is < 8!"); + LogMessage("GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: %d is < 8!", mGLMaxTextureUnits); return PR_FALSE; }