From c830bece191e469aa6499b500244bb351710c49f Mon Sep 17 00:00:00 2001 From: Saurabh Anand Date: Sun, 9 Dec 2012 17:28:41 +0530 Subject: [PATCH] Bug 816173 - Split out WebGLFramebuffer into separate files --- content/canvas/src/WebGLContext.h | 464 +----------------------- content/canvas/src/WebGLContextGL.cpp | 2 +- content/canvas/src/WebGLFramebuffer.cpp | 355 ++++++++++++++++++ content/canvas/src/WebGLFramebuffer.h | 181 +++++++++ 4 files changed, 538 insertions(+), 464 deletions(-) create mode 100644 content/canvas/src/WebGLFramebuffer.h diff --git a/content/canvas/src/WebGLContext.h b/content/canvas/src/WebGLContext.h index 92333119f99..63b2a8effc3 100644 --- a/content/canvas/src/WebGLContext.h +++ b/content/canvas/src/WebGLContext.h @@ -12,6 +12,7 @@ #include "WebGLBuffer.h" #include "WebGLProgram.h" #include "WebGLUniformLocation.h" +#include "WebGLFramebuffer.h" #include "WebGLRenderbuffer.h" #include "WebGLTexture.h" #include "WebGLVertexAttribData.h" @@ -71,7 +72,6 @@ class nsIPropertyBag; namespace mozilla { -class WebGLFramebuffer; class WebGLMemoryPressureObserver; class WebGLContextBoundObject; class WebGLActiveInfo; @@ -1159,468 +1159,6 @@ ToSupports(WebGLContext* context) return static_cast(context); } -class WebGLFramebufferAttachment -{ - // deleting a texture or renderbuffer immediately detaches it - WebGLRefPtr mTexturePtr; - WebGLRefPtr mRenderbufferPtr; - WebGLenum mAttachmentPoint; - WebGLint mTextureLevel; - WebGLenum mTextureCubeMapFace; - - friend class WebGLFramebuffer; - -public: - WebGLFramebufferAttachment(WebGLenum aAttachmentPoint) - : mAttachmentPoint(aAttachmentPoint) - {} - - bool IsDefined() const { - return Texture() || Renderbuffer(); - } - - bool IsDeleteRequested() const { - return Texture() ? Texture()->IsDeleteRequested() - : Renderbuffer() ? Renderbuffer()->IsDeleteRequested() - : false; - } - - bool HasAlpha() const { - WebGLenum format = 0; - if (Texture() && Texture()->HasImageInfoAt(mTextureLevel, mTextureCubeMapFace)) - format = Texture()->ImageInfoAt(mTextureLevel, mTextureCubeMapFace).Format(); - else if (Renderbuffer()) - format = Renderbuffer()->InternalFormat(); - return format == LOCAL_GL_RGBA || - format == LOCAL_GL_LUMINANCE_ALPHA || - format == LOCAL_GL_ALPHA || - format == LOCAL_GL_RGBA4 || - format == LOCAL_GL_RGB5_A1; - } - - void SetTexture(WebGLTexture *tex, WebGLint level, WebGLenum face) { - mTexturePtr = tex; - mRenderbufferPtr = nullptr; - mTextureLevel = level; - mTextureCubeMapFace = face; - } - void SetRenderbuffer(WebGLRenderbuffer *rb) { - mTexturePtr = nullptr; - mRenderbufferPtr = rb; - } - const WebGLTexture *Texture() const { - return mTexturePtr; - } - WebGLTexture *Texture() { - return mTexturePtr; - } - const WebGLRenderbuffer *Renderbuffer() const { - return mRenderbufferPtr; - } - WebGLRenderbuffer *Renderbuffer() { - return mRenderbufferPtr; - } - WebGLint TextureLevel() const { - return mTextureLevel; - } - WebGLenum TextureCubeMapFace() const { - return mTextureCubeMapFace; - } - - bool HasUninitializedRenderbuffer() const { - return mRenderbufferPtr && !mRenderbufferPtr->Initialized(); - } - - void Reset() { - mTexturePtr = nullptr; - mRenderbufferPtr = nullptr; - } - - const WebGLRectangleObject* RectangleObject() const { - if (Texture() && Texture()->HasImageInfoAt(mTextureLevel, mTextureCubeMapFace)) - return &Texture()->ImageInfoAt(mTextureLevel, mTextureCubeMapFace); - else if (Renderbuffer()) - return Renderbuffer(); - else - return nullptr; - } - bool HasSameDimensionsAs(const WebGLFramebufferAttachment& other) const { - const WebGLRectangleObject *thisRect = RectangleObject(); - const WebGLRectangleObject *otherRect = other.RectangleObject(); - return thisRect && - otherRect && - thisRect->HasSameDimensionsAs(*otherRect); - } - - bool IsComplete() const { - const WebGLRectangleObject *thisRect = RectangleObject(); - - if (!thisRect || - !thisRect->Width() || - !thisRect->Height()) - return false; - - if (mTexturePtr) { - if (!mTexturePtr->HasImageInfoAt(0, 0)) - return false; - - WebGLenum format = mTexturePtr->ImageInfoAt(0).Format(); - switch (mAttachmentPoint) - { - case LOCAL_GL_COLOR_ATTACHMENT0: - return format == LOCAL_GL_ALPHA || - format == LOCAL_GL_LUMINANCE || - format == LOCAL_GL_LUMINANCE_ALPHA || - format == LOCAL_GL_RGB || - format == LOCAL_GL_RGBA; - case LOCAL_GL_DEPTH_ATTACHMENT: - return format == LOCAL_GL_DEPTH_COMPONENT; - case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT: - return format == LOCAL_GL_DEPTH_STENCIL; - - default: - MOZ_NOT_REACHED("Invalid WebGL texture format?"); - } - } - - if (mRenderbufferPtr) { - WebGLenum format = mRenderbufferPtr->InternalFormat(); - switch (mAttachmentPoint) { - case LOCAL_GL_COLOR_ATTACHMENT0: - return format == LOCAL_GL_RGB565 || - format == LOCAL_GL_RGB5_A1 || - format == LOCAL_GL_RGBA4; - case LOCAL_GL_DEPTH_ATTACHMENT: - return format == LOCAL_GL_DEPTH_COMPONENT16; - case LOCAL_GL_STENCIL_ATTACHMENT: - return format == LOCAL_GL_STENCIL_INDEX8; - case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT: - return format == LOCAL_GL_DEPTH_STENCIL; - default: - NS_ABORT(); // should have been validated earlier - } - } - - NS_ABORT(); // should never get there - return false; - } -}; - -class WebGLFramebuffer MOZ_FINAL - : public nsISupports - , public WebGLRefCountedObject - , public LinkedListElement - , public WebGLContextBoundObject - , public nsWrapperCache -{ -public: - WebGLFramebuffer(WebGLContext *context) - : WebGLContextBoundObject(context) - , mHasEverBeenBound(false) - , mColorAttachment(LOCAL_GL_COLOR_ATTACHMENT0) - , mDepthAttachment(LOCAL_GL_DEPTH_ATTACHMENT) - , mStencilAttachment(LOCAL_GL_STENCIL_ATTACHMENT) - , mDepthStencilAttachment(LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) - { - SetIsDOMBinding(); - mContext->MakeContextCurrent(); - mContext->gl->fGenFramebuffers(1, &mGLName); - mContext->mFramebuffers.insertBack(this); - } - - ~WebGLFramebuffer() { - DeleteOnce(); - } - - void Delete() { - mColorAttachment.Reset(); - mDepthAttachment.Reset(); - mStencilAttachment.Reset(); - mDepthStencilAttachment.Reset(); - mContext->MakeContextCurrent(); - mContext->gl->fDeleteFramebuffers(1, &mGLName); - LinkedListElement::removeFrom(mContext->mFramebuffers); - } - - bool HasEverBeenBound() { return mHasEverBeenBound; } - void SetHasEverBeenBound(bool x) { mHasEverBeenBound = x; } - WebGLuint GLName() { return mGLName; } - - void FramebufferRenderbuffer(WebGLenum target, - WebGLenum attachment, - WebGLenum rbtarget, - WebGLRenderbuffer *wrb) - { - if (!mContext->ValidateObjectAllowNull("framebufferRenderbuffer: renderbuffer", wrb)) - { - return; - } - - if (target != LOCAL_GL_FRAMEBUFFER) - return mContext->ErrorInvalidEnumInfo("framebufferRenderbuffer: target", target); - - if (rbtarget != LOCAL_GL_RENDERBUFFER) - return mContext->ErrorInvalidEnumInfo("framebufferRenderbuffer: renderbuffer target:", rbtarget); - - switch (attachment) { - case LOCAL_GL_DEPTH_ATTACHMENT: - mDepthAttachment.SetRenderbuffer(wrb); - break; - case LOCAL_GL_STENCIL_ATTACHMENT: - mStencilAttachment.SetRenderbuffer(wrb); - break; - case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT: - mDepthStencilAttachment.SetRenderbuffer(wrb); - break; - default: - // finish checking that the 'attachment' parameter is among the allowed values - if (attachment != LOCAL_GL_COLOR_ATTACHMENT0) - return mContext->ErrorInvalidEnumInfo("framebufferRenderbuffer: attachment", attachment); - - mColorAttachment.SetRenderbuffer(wrb); - break; - } - - mContext->MakeContextCurrent(); - WebGLuint parambuffername = wrb ? wrb->GLName() : 0; - if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { - WebGLuint depthbuffername = parambuffername; - WebGLuint stencilbuffername = parambuffername; - if (!parambuffername){ - depthbuffername = mDepthAttachment.Renderbuffer() ? mDepthAttachment.Renderbuffer()->GLName() : 0; - stencilbuffername = mStencilAttachment.Renderbuffer() ? mStencilAttachment.Renderbuffer()->GLName() : 0; - } - mContext->gl->fFramebufferRenderbuffer(target, LOCAL_GL_DEPTH_ATTACHMENT, rbtarget, depthbuffername); - mContext->gl->fFramebufferRenderbuffer(target, LOCAL_GL_STENCIL_ATTACHMENT, rbtarget, stencilbuffername); - } else { - WebGLuint renderbuffername = parambuffername; - if(!parambuffername && (attachment == LOCAL_GL_DEPTH_ATTACHMENT || attachment == LOCAL_GL_STENCIL_ATTACHMENT)){ - renderbuffername = mDepthStencilAttachment.Renderbuffer() ? mDepthStencilAttachment.Renderbuffer()->GLName() : 0; - } - mContext->gl->fFramebufferRenderbuffer(target, attachment, rbtarget, renderbuffername); - } - } - - void FramebufferTexture2D(WebGLenum target, - WebGLenum attachment, - WebGLenum textarget, - WebGLTexture *wtex, - WebGLint level) - { - if (!mContext->ValidateObjectAllowNull("framebufferTexture2D: texture", - wtex)) - { - return; - } - - if (target != LOCAL_GL_FRAMEBUFFER) - return mContext->ErrorInvalidEnumInfo("framebufferTexture2D: target", target); - - if (textarget != LOCAL_GL_TEXTURE_2D && - (textarget < LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X || - textarget > LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)) - return mContext->ErrorInvalidEnumInfo("framebufferTexture2D: invalid texture target", textarget); - - if (level != 0) - return mContext->ErrorInvalidValue("framebufferTexture2D: level must be 0"); - - size_t face = WebGLTexture::FaceForTarget(textarget); - switch (attachment) { - case LOCAL_GL_DEPTH_ATTACHMENT: - mDepthAttachment.SetTexture(wtex, level, face); - break; - case LOCAL_GL_STENCIL_ATTACHMENT: - mStencilAttachment.SetTexture(wtex, level, face); - break; - case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT: - mDepthStencilAttachment.SetTexture(wtex, level, face); - break; - default: - if (attachment != LOCAL_GL_COLOR_ATTACHMENT0) - return mContext->ErrorInvalidEnumInfo("framebufferTexture2D: attachment", attachment); - - mColorAttachment.SetTexture(wtex, level, face); - break; - } - - mContext->MakeContextCurrent(); - WebGLuint paramtexturename = wtex ? wtex->GLName() : 0; - if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { - WebGLuint depthtexturename = paramtexturename; - WebGLuint stenciltexturename = paramtexturename; - if(!paramtexturename){ - depthtexturename = mDepthAttachment.Texture() ? mDepthAttachment.Texture()->GLName() : 0; - stenciltexturename = mStencilAttachment.Texture() ? mStencilAttachment.Texture()->GLName() : 0; - } - mContext->gl->fFramebufferTexture2D(target, LOCAL_GL_DEPTH_ATTACHMENT, textarget, depthtexturename, level); - mContext->gl->fFramebufferTexture2D(target, LOCAL_GL_STENCIL_ATTACHMENT, textarget, stenciltexturename, level); - } else { - WebGLuint texturename = paramtexturename; - if(!paramtexturename && (attachment == LOCAL_GL_DEPTH_ATTACHMENT || attachment == LOCAL_GL_STENCIL_ATTACHMENT)){ - texturename = mDepthStencilAttachment.Texture() ? mDepthStencilAttachment.Texture()->GLName() : 0; - } - mContext->gl->fFramebufferTexture2D(target, attachment, textarget, texturename, level); - } - - return; - } - - bool HasIncompleteAttachment() const { - return (mColorAttachment.IsDefined() && !mColorAttachment.IsComplete()) || - (mDepthAttachment.IsDefined() && !mDepthAttachment.IsComplete()) || - (mStencilAttachment.IsDefined() && !mStencilAttachment.IsComplete()) || - (mDepthStencilAttachment.IsDefined() && !mDepthStencilAttachment.IsComplete()); - } - - bool HasDepthStencilConflict() const { - return int(mDepthAttachment.IsDefined()) + - int(mStencilAttachment.IsDefined()) + - int(mDepthStencilAttachment.IsDefined()) >= 2; - } - - bool HasAttachmentsOfMismatchedDimensions() const { - return (mDepthAttachment.IsDefined() && !mDepthAttachment.HasSameDimensionsAs(mColorAttachment)) || - (mStencilAttachment.IsDefined() && !mStencilAttachment.HasSameDimensionsAs(mColorAttachment)) || - (mDepthStencilAttachment.IsDefined() && !mDepthStencilAttachment.HasSameDimensionsAs(mColorAttachment)); - } - - const WebGLFramebufferAttachment& ColorAttachment() const { - return mColorAttachment; - } - - const WebGLFramebufferAttachment& DepthAttachment() const { - return mDepthAttachment; - } - - const WebGLFramebufferAttachment& StencilAttachment() const { - return mStencilAttachment; - } - - const WebGLFramebufferAttachment& DepthStencilAttachment() const { - return mDepthStencilAttachment; - } - - const WebGLFramebufferAttachment& GetAttachment(WebGLenum attachment) const { - if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) - return mDepthStencilAttachment; - if (attachment == LOCAL_GL_DEPTH_ATTACHMENT) - return mDepthAttachment; - if (attachment == LOCAL_GL_STENCIL_ATTACHMENT) - return mStencilAttachment; - - NS_ASSERTION(attachment == LOCAL_GL_COLOR_ATTACHMENT0, "bad attachment!"); - return mColorAttachment; - } - - void DetachTexture(const WebGLTexture *tex) { - if (mColorAttachment.Texture() == tex) - FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, LOCAL_GL_TEXTURE_2D, nullptr, 0); - if (mDepthAttachment.Texture() == tex) - FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT, LOCAL_GL_TEXTURE_2D, nullptr, 0); - if (mStencilAttachment.Texture() == tex) - FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT, LOCAL_GL_TEXTURE_2D, nullptr, 0); - if (mDepthStencilAttachment.Texture() == tex) - FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT, LOCAL_GL_TEXTURE_2D, nullptr, 0); - } - - void DetachRenderbuffer(const WebGLRenderbuffer *rb) { - if (mColorAttachment.Renderbuffer() == rb) - FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, LOCAL_GL_RENDERBUFFER, nullptr); - if (mDepthAttachment.Renderbuffer() == rb) - FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT, LOCAL_GL_RENDERBUFFER, nullptr); - if (mStencilAttachment.Renderbuffer() == rb) - FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT, LOCAL_GL_RENDERBUFFER, nullptr); - if (mDepthStencilAttachment.Renderbuffer() == rb) - FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT, LOCAL_GL_RENDERBUFFER, nullptr); - } - - const WebGLRectangleObject *RectangleObject() { - return mColorAttachment.RectangleObject(); - } - - WebGLContext *GetParentObject() const { - return Context(); - } - - virtual JSObject* WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap); - - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WebGLFramebuffer) - - bool CheckAndInitializeRenderbuffers() - { - // enforce WebGL section 6.5 which is WebGL-specific, hence OpenGL itself would not - // generate the INVALID_FRAMEBUFFER_OPERATION that we need here - if (HasDepthStencilConflict()) - return false; - - if (HasIncompleteAttachment()) - return false; - - if (!mColorAttachment.HasUninitializedRenderbuffer() && - !mDepthAttachment.HasUninitializedRenderbuffer() && - !mStencilAttachment.HasUninitializedRenderbuffer() && - !mDepthStencilAttachment.HasUninitializedRenderbuffer()) - return true; - - // ensure INVALID_FRAMEBUFFER_OPERATION in zero-size case - const WebGLRectangleObject *rect = mColorAttachment.RectangleObject(); - if (!rect || - !rect->Width() || - !rect->Height()) - return false; - - mContext->MakeContextCurrent(); - - WebGLenum status = mContext->CheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER); - if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) - return false; - - uint32_t mask = 0; - - if (mColorAttachment.HasUninitializedRenderbuffer()) - mask |= LOCAL_GL_COLOR_BUFFER_BIT; - - if (mDepthAttachment.HasUninitializedRenderbuffer() || - mDepthStencilAttachment.HasUninitializedRenderbuffer()) - { - mask |= LOCAL_GL_DEPTH_BUFFER_BIT; - } - - if (mStencilAttachment.HasUninitializedRenderbuffer() || - mDepthStencilAttachment.HasUninitializedRenderbuffer()) - { - mask |= LOCAL_GL_STENCIL_BUFFER_BIT; - } - - mContext->ForceClearFramebufferWithDefaultValues(mask, nsIntRect(0, 0, rect->Width(), rect->Height())); - - if (mColorAttachment.HasUninitializedRenderbuffer()) - mColorAttachment.Renderbuffer()->SetInitialized(true); - - if (mDepthAttachment.HasUninitializedRenderbuffer()) - mDepthAttachment.Renderbuffer()->SetInitialized(true); - - if (mStencilAttachment.HasUninitializedRenderbuffer()) - mStencilAttachment.Renderbuffer()->SetInitialized(true); - - if (mDepthStencilAttachment.HasUninitializedRenderbuffer()) - mDepthStencilAttachment.Renderbuffer()->SetInitialized(true); - - return true; - } - - WebGLuint mGLName; - bool mHasEverBeenBound; - - // we only store pointers to attached renderbuffers, not to attached textures, because - // we will only need to initialize renderbuffers. Textures are already initialized. - WebGLFramebufferAttachment mColorAttachment, - mDepthAttachment, - mStencilAttachment, - mDepthStencilAttachment; -}; - class WebGLActiveInfo MOZ_FINAL : public nsISupports { diff --git a/content/canvas/src/WebGLContextGL.cpp b/content/canvas/src/WebGLContextGL.cpp index 95abdb3b2b9..5e3ab126dab 100644 --- a/content/canvas/src/WebGLContextGL.cpp +++ b/content/canvas/src/WebGLContextGL.cpp @@ -2251,7 +2251,7 @@ WebGLContext::GetFramebufferAttachmentParameter(JSContext* cx, MakeContextCurrent(); - const WebGLFramebufferAttachment& fba = mBoundFramebuffer->GetAttachment(attachment); + const WebGLFramebuffer::Attachment& fba = mBoundFramebuffer->GetAttachment(attachment); if (fba.Renderbuffer()) { switch (pname) { diff --git a/content/canvas/src/WebGLFramebuffer.cpp b/content/canvas/src/WebGLFramebuffer.cpp index ba1910a2b8c..6e1ae3b7843 100644 --- a/content/canvas/src/WebGLFramebuffer.cpp +++ b/content/canvas/src/WebGLFramebuffer.cpp @@ -4,6 +4,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "WebGLContext.h" +#include "WebGLFramebuffer.h" #include "mozilla/dom/WebGLRenderingContextBinding.h" using namespace mozilla; @@ -13,6 +14,360 @@ WebGLFramebuffer::WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap) return dom::WebGLFramebufferBinding::Wrap(cx, scope, this, triedToWrap); } +WebGLFramebuffer::WebGLFramebuffer(WebGLContext *context) + : WebGLContextBoundObject(context) + , mHasEverBeenBound(false) + , mColorAttachment(LOCAL_GL_COLOR_ATTACHMENT0) + , mDepthAttachment(LOCAL_GL_DEPTH_ATTACHMENT) + , mStencilAttachment(LOCAL_GL_STENCIL_ATTACHMENT) + , mDepthStencilAttachment(LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) +{ + SetIsDOMBinding(); + mContext->MakeContextCurrent(); + mContext->gl->fGenFramebuffers(1, &mGLName); + mContext->mFramebuffers.insertBack(this); +} + +bool +WebGLFramebuffer::Attachment::IsDeleteRequested() const { + return Texture() ? Texture()->IsDeleteRequested() + : Renderbuffer() ? Renderbuffer()->IsDeleteRequested() + : false; +} + +bool +WebGLFramebuffer::Attachment::HasAlpha() const { + WebGLenum format = 0; + if (Texture() && Texture()->HasImageInfoAt(mTextureLevel, mTextureCubeMapFace)) + format = Texture()->ImageInfoAt(mTextureLevel, mTextureCubeMapFace).Format(); + else if (Renderbuffer()) + format = Renderbuffer()->InternalFormat(); + return format == LOCAL_GL_RGBA || + format == LOCAL_GL_LUMINANCE_ALPHA || + format == LOCAL_GL_ALPHA || + format == LOCAL_GL_RGBA4 || + format == LOCAL_GL_RGB5_A1; +} + +void +WebGLFramebuffer::Attachment::SetTexture(WebGLTexture *tex, WebGLint level, WebGLenum face) { + mTexturePtr = tex; + mRenderbufferPtr = nullptr; + mTextureLevel = level; + mTextureCubeMapFace = face; +} + +bool +WebGLFramebuffer::Attachment::HasUninitializedRenderbuffer() const { + return mRenderbufferPtr && !mRenderbufferPtr->Initialized(); +} + +const WebGLRectangleObject* +WebGLFramebuffer::Attachment::RectangleObject() const { + if (Texture() && Texture()->HasImageInfoAt(mTextureLevel, mTextureCubeMapFace)) + return &Texture()->ImageInfoAt(mTextureLevel, mTextureCubeMapFace); + else if (Renderbuffer()) + return Renderbuffer(); + else + return nullptr; +} + +bool +WebGLFramebuffer::Attachment::HasSameDimensionsAs(const Attachment& other) const { + const WebGLRectangleObject *thisRect = RectangleObject(); + const WebGLRectangleObject *otherRect = other.RectangleObject(); + return thisRect && + otherRect && + thisRect->HasSameDimensionsAs(*otherRect); +} + +bool +WebGLFramebuffer::Attachment::IsComplete() const { + const WebGLRectangleObject *thisRect = RectangleObject(); + + if (!thisRect || + !thisRect->Width() || + !thisRect->Height()) + return false; + + if (mTexturePtr) { + if (!mTexturePtr->HasImageInfoAt(0, 0)) + return false; + + WebGLenum format = mTexturePtr->ImageInfoAt(0).Format(); + switch (mAttachmentPoint) + { + case LOCAL_GL_COLOR_ATTACHMENT0: + return format == LOCAL_GL_ALPHA || + format == LOCAL_GL_LUMINANCE || + format == LOCAL_GL_LUMINANCE_ALPHA || + format == LOCAL_GL_RGB || + format == LOCAL_GL_RGBA; + case LOCAL_GL_DEPTH_ATTACHMENT: + return format == LOCAL_GL_DEPTH_COMPONENT; + case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT: + return format == LOCAL_GL_DEPTH_STENCIL; + + default: + MOZ_NOT_REACHED("Invalid WebGL texture format?"); + } + } + + if (mRenderbufferPtr) { + WebGLenum format = mRenderbufferPtr->InternalFormat(); + switch (mAttachmentPoint) { + case LOCAL_GL_COLOR_ATTACHMENT0: + return format == LOCAL_GL_RGB565 || + format == LOCAL_GL_RGB5_A1 || + format == LOCAL_GL_RGBA4; + case LOCAL_GL_DEPTH_ATTACHMENT: + return format == LOCAL_GL_DEPTH_COMPONENT16; + case LOCAL_GL_STENCIL_ATTACHMENT: + return format == LOCAL_GL_STENCIL_INDEX8; + case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT: + return format == LOCAL_GL_DEPTH_STENCIL; + default: + NS_ABORT(); // should have been validated earlier + } + } + + NS_ABORT(); // should never get there + return false; +} + +void +WebGLFramebuffer::Delete() { + mColorAttachment.Reset(); + mDepthAttachment.Reset(); + mStencilAttachment.Reset(); + mDepthStencilAttachment.Reset(); + mContext->MakeContextCurrent(); + mContext->gl->fDeleteFramebuffers(1, &mGLName); + LinkedListElement::removeFrom(mContext->mFramebuffers); +} + +void +WebGLFramebuffer::FramebufferRenderbuffer(WebGLenum target, + WebGLenum attachment, + WebGLenum rbtarget, + WebGLRenderbuffer *wrb) +{ + if (!mContext->ValidateObjectAllowNull("framebufferRenderbuffer: renderbuffer", wrb)) + { + return; + } + + if (target != LOCAL_GL_FRAMEBUFFER) + return mContext->ErrorInvalidEnumInfo("framebufferRenderbuffer: target", target); + + if (rbtarget != LOCAL_GL_RENDERBUFFER) + return mContext->ErrorInvalidEnumInfo("framebufferRenderbuffer: renderbuffer target:", rbtarget); + + switch (attachment) { + case LOCAL_GL_DEPTH_ATTACHMENT: + mDepthAttachment.SetRenderbuffer(wrb); + break; + case LOCAL_GL_STENCIL_ATTACHMENT: + mStencilAttachment.SetRenderbuffer(wrb); + break; + case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT: + mDepthStencilAttachment.SetRenderbuffer(wrb); + break; + default: + // finish checking that the 'attachment' parameter is among the allowed values + if (attachment != LOCAL_GL_COLOR_ATTACHMENT0) + return mContext->ErrorInvalidEnumInfo("framebufferRenderbuffer: attachment", attachment); + + mColorAttachment.SetRenderbuffer(wrb); + break; + } + + mContext->MakeContextCurrent(); + WebGLuint parambuffername = wrb ? wrb->GLName() : 0; + if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { + WebGLuint depthbuffername = parambuffername; + WebGLuint stencilbuffername = parambuffername; + if (!parambuffername){ + depthbuffername = mDepthAttachment.Renderbuffer() ? mDepthAttachment.Renderbuffer()->GLName() : 0; + stencilbuffername = mStencilAttachment.Renderbuffer() ? mStencilAttachment.Renderbuffer()->GLName() : 0; + } + mContext->gl->fFramebufferRenderbuffer(target, LOCAL_GL_DEPTH_ATTACHMENT, rbtarget, depthbuffername); + mContext->gl->fFramebufferRenderbuffer(target, LOCAL_GL_STENCIL_ATTACHMENT, rbtarget, stencilbuffername); + } else { + WebGLuint renderbuffername = parambuffername; + if(!parambuffername && (attachment == LOCAL_GL_DEPTH_ATTACHMENT || attachment == LOCAL_GL_STENCIL_ATTACHMENT)){ + renderbuffername = mDepthStencilAttachment.Renderbuffer() ? mDepthStencilAttachment.Renderbuffer()->GLName() : 0; + } + mContext->gl->fFramebufferRenderbuffer(target, attachment, rbtarget, renderbuffername); + } +} + +void +WebGLFramebuffer::FramebufferTexture2D(WebGLenum target, + WebGLenum attachment, + WebGLenum textarget, + WebGLTexture *wtex, + WebGLint level) +{ + if (!mContext->ValidateObjectAllowNull("framebufferTexture2D: texture", + wtex)) + { + return; + } + + if (target != LOCAL_GL_FRAMEBUFFER) + return mContext->ErrorInvalidEnumInfo("framebufferTexture2D: target", target); + + if (textarget != LOCAL_GL_TEXTURE_2D && + (textarget < LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X || + textarget > LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)) + return mContext->ErrorInvalidEnumInfo("framebufferTexture2D: invalid texture target", textarget); + + if (level != 0) + return mContext->ErrorInvalidValue("framebufferTexture2D: level must be 0"); + + size_t face = WebGLTexture::FaceForTarget(textarget); + switch (attachment) { + case LOCAL_GL_DEPTH_ATTACHMENT: + mDepthAttachment.SetTexture(wtex, level, face); + break; + case LOCAL_GL_STENCIL_ATTACHMENT: + mStencilAttachment.SetTexture(wtex, level, face); + break; + case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT: + mDepthStencilAttachment.SetTexture(wtex, level, face); + break; + default: + if (attachment != LOCAL_GL_COLOR_ATTACHMENT0) + return mContext->ErrorInvalidEnumInfo("framebufferTexture2D: attachment", attachment); + + mColorAttachment.SetTexture(wtex, level, face); + break; + } + + mContext->MakeContextCurrent(); + WebGLuint paramtexturename = wtex ? wtex->GLName() : 0; + if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { + WebGLuint depthtexturename = paramtexturename; + WebGLuint stenciltexturename = paramtexturename; + if(!paramtexturename){ + depthtexturename = mDepthAttachment.Texture() ? mDepthAttachment.Texture()->GLName() : 0; + stenciltexturename = mStencilAttachment.Texture() ? mStencilAttachment.Texture()->GLName() : 0; + } + mContext->gl->fFramebufferTexture2D(target, LOCAL_GL_DEPTH_ATTACHMENT, textarget, depthtexturename, level); + mContext->gl->fFramebufferTexture2D(target, LOCAL_GL_STENCIL_ATTACHMENT, textarget, stenciltexturename, level); + } else { + WebGLuint texturename = paramtexturename; + if(!paramtexturename && (attachment == LOCAL_GL_DEPTH_ATTACHMENT || attachment == LOCAL_GL_STENCIL_ATTACHMENT)){ + texturename = mDepthStencilAttachment.Texture() ? mDepthStencilAttachment.Texture()->GLName() : 0; + } + mContext->gl->fFramebufferTexture2D(target, attachment, textarget, texturename, level); + } + + return; +} + +const WebGLFramebuffer::Attachment& +WebGLFramebuffer::GetAttachment(WebGLenum attachment) const { + if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) + return mDepthStencilAttachment; + if (attachment == LOCAL_GL_DEPTH_ATTACHMENT) + return mDepthAttachment; + if (attachment == LOCAL_GL_STENCIL_ATTACHMENT) + return mStencilAttachment; + + NS_ASSERTION(attachment == LOCAL_GL_COLOR_ATTACHMENT0, "bad attachment!"); + return mColorAttachment; +} + +void +WebGLFramebuffer::DetachTexture(const WebGLTexture *tex) { + if (mColorAttachment.Texture() == tex) + FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, LOCAL_GL_TEXTURE_2D, nullptr, 0); + if (mDepthAttachment.Texture() == tex) + FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT, LOCAL_GL_TEXTURE_2D, nullptr, 0); + if (mStencilAttachment.Texture() == tex) + FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT, LOCAL_GL_TEXTURE_2D, nullptr, 0); + if (mDepthStencilAttachment.Texture() == tex) + FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT, LOCAL_GL_TEXTURE_2D, nullptr, 0); +} + +void +WebGLFramebuffer::DetachRenderbuffer(const WebGLRenderbuffer *rb) { + if (mColorAttachment.Renderbuffer() == rb) + FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, LOCAL_GL_RENDERBUFFER, nullptr); + if (mDepthAttachment.Renderbuffer() == rb) + FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT, LOCAL_GL_RENDERBUFFER, nullptr); + if (mStencilAttachment.Renderbuffer() == rb) + FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT, LOCAL_GL_RENDERBUFFER, nullptr); + if (mDepthStencilAttachment.Renderbuffer() == rb) + FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT, LOCAL_GL_RENDERBUFFER, nullptr); +} + +bool +WebGLFramebuffer::CheckAndInitializeRenderbuffers() +{ + // enforce WebGL section 6.5 which is WebGL-specific, hence OpenGL itself would not + // generate the INVALID_FRAMEBUFFER_OPERATION that we need here + if (HasDepthStencilConflict()) + return false; + + if (HasIncompleteAttachment()) + return false; + + if (!mColorAttachment.HasUninitializedRenderbuffer() && + !mDepthAttachment.HasUninitializedRenderbuffer() && + !mStencilAttachment.HasUninitializedRenderbuffer() && + !mDepthStencilAttachment.HasUninitializedRenderbuffer()) + return true; + + // ensure INVALID_FRAMEBUFFER_OPERATION in zero-size case + const WebGLRectangleObject *rect = mColorAttachment.RectangleObject(); + if (!rect || + !rect->Width() || + !rect->Height()) + return false; + + mContext->MakeContextCurrent(); + + WebGLenum status = mContext->CheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER); + if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) + return false; + + uint32_t mask = 0; + + if (mColorAttachment.HasUninitializedRenderbuffer()) + mask |= LOCAL_GL_COLOR_BUFFER_BIT; + + if (mDepthAttachment.HasUninitializedRenderbuffer() || + mDepthStencilAttachment.HasUninitializedRenderbuffer()) + { + mask |= LOCAL_GL_DEPTH_BUFFER_BIT; + } + + if (mStencilAttachment.HasUninitializedRenderbuffer() || + mDepthStencilAttachment.HasUninitializedRenderbuffer()) + { + mask |= LOCAL_GL_STENCIL_BUFFER_BIT; + } + + mContext->ForceClearFramebufferWithDefaultValues(mask, nsIntRect(0, 0, rect->Width(), rect->Height())); + + if (mColorAttachment.HasUninitializedRenderbuffer()) + mColorAttachment.Renderbuffer()->SetInitialized(true); + + if (mDepthAttachment.HasUninitializedRenderbuffer()) + mDepthAttachment.Renderbuffer()->SetInitialized(true); + + if (mStencilAttachment.HasUninitializedRenderbuffer()) + mStencilAttachment.Renderbuffer()->SetInitialized(true); + + if (mDepthStencilAttachment.HasUninitializedRenderbuffer()) + mDepthStencilAttachment.Renderbuffer()->SetInitialized(true); + + return true; +} + NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_8(WebGLFramebuffer, mColorAttachment.mTexturePtr, mColorAttachment.mRenderbufferPtr, diff --git a/content/canvas/src/WebGLFramebuffer.h b/content/canvas/src/WebGLFramebuffer.h new file mode 100644 index 00000000000..e669a5c35c8 --- /dev/null +++ b/content/canvas/src/WebGLFramebuffer.h @@ -0,0 +1,181 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef WEBGLFRAMEBUFFER_H_ +#define WEBGLFRAMEBUFFER_H_ + +#include "WebGLObjectModel.h" + +#include "nsWrapperCache.h" + +#include "mozilla/LinkedList.h" + +namespace mozilla { + +class WebGLTexture; +class WebGLRenderbuffer; + +class WebGLFramebuffer MOZ_FINAL + : public nsISupports + , public WebGLRefCountedObject + , public LinkedListElement + , public WebGLContextBoundObject + , public nsWrapperCache +{ +public: + WebGLFramebuffer(WebGLContext *context); + + ~WebGLFramebuffer() { + DeleteOnce(); + } + + class Attachment + { + // deleting a texture or renderbuffer immediately detaches it + WebGLRefPtr mTexturePtr; + WebGLRefPtr mRenderbufferPtr; + WebGLenum mAttachmentPoint; + WebGLint mTextureLevel; + WebGLenum mTextureCubeMapFace; + + friend class WebGLFramebuffer; + + public: + Attachment(WebGLenum aAttachmentPoint) + : mAttachmentPoint(aAttachmentPoint) + {} + + bool IsDefined() const { + return Texture() || Renderbuffer(); + } + + bool IsDeleteRequested() const; + + bool HasAlpha() const; + + void SetTexture(WebGLTexture *tex, WebGLint level, WebGLenum face); + void SetRenderbuffer(WebGLRenderbuffer *rb) { + mTexturePtr = nullptr; + mRenderbufferPtr = rb; + } + const WebGLTexture *Texture() const { + return mTexturePtr; + } + WebGLTexture *Texture() { + return mTexturePtr; + } + const WebGLRenderbuffer *Renderbuffer() const { + return mRenderbufferPtr; + } + WebGLRenderbuffer *Renderbuffer() { + return mRenderbufferPtr; + } + WebGLint TextureLevel() const { + return mTextureLevel; + } + WebGLenum TextureCubeMapFace() const { + return mTextureCubeMapFace; + } + + bool HasUninitializedRenderbuffer() const; + + void Reset() { + mTexturePtr = nullptr; + mRenderbufferPtr = nullptr; + } + + const WebGLRectangleObject* RectangleObject() const; + bool HasSameDimensionsAs(const Attachment& other) const; + + bool IsComplete() const; + }; + + void Delete(); + + bool HasEverBeenBound() { return mHasEverBeenBound; } + void SetHasEverBeenBound(bool x) { mHasEverBeenBound = x; } + WebGLuint GLName() { return mGLName; } + + void FramebufferRenderbuffer(WebGLenum target, + WebGLenum attachment, + WebGLenum rbtarget, + WebGLRenderbuffer *wrb); + + void FramebufferTexture2D(WebGLenum target, + WebGLenum attachment, + WebGLenum textarget, + WebGLTexture *wtex, + WebGLint level); + + bool HasIncompleteAttachment() const { + return (mColorAttachment.IsDefined() && !mColorAttachment.IsComplete()) || + (mDepthAttachment.IsDefined() && !mDepthAttachment.IsComplete()) || + (mStencilAttachment.IsDefined() && !mStencilAttachment.IsComplete()) || + (mDepthStencilAttachment.IsDefined() && !mDepthStencilAttachment.IsComplete()); + } + + bool HasDepthStencilConflict() const { + return int(mDepthAttachment.IsDefined()) + + int(mStencilAttachment.IsDefined()) + + int(mDepthStencilAttachment.IsDefined()) >= 2; + } + + bool HasAttachmentsOfMismatchedDimensions() const { + return (mDepthAttachment.IsDefined() && !mDepthAttachment.HasSameDimensionsAs(mColorAttachment)) || + (mStencilAttachment.IsDefined() && !mStencilAttachment.HasSameDimensionsAs(mColorAttachment)) || + (mDepthStencilAttachment.IsDefined() && !mDepthStencilAttachment.HasSameDimensionsAs(mColorAttachment)); + } + + const Attachment& ColorAttachment() const { + return mColorAttachment; + } + + const Attachment& DepthAttachment() const { + return mDepthAttachment; + } + + const Attachment& StencilAttachment() const { + return mStencilAttachment; + } + + const Attachment& DepthStencilAttachment() const { + return mDepthStencilAttachment; + } + + const Attachment& GetAttachment(WebGLenum attachment) const; + + void DetachTexture(const WebGLTexture *tex); + + void DetachRenderbuffer(const WebGLRenderbuffer *rb); + + const WebGLRectangleObject *RectangleObject() { + return mColorAttachment.RectangleObject(); + } + + WebGLContext *GetParentObject() const { + return Context(); + } + + virtual JSObject* WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap); + + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WebGLFramebuffer) + + bool CheckAndInitializeRenderbuffers(); + + WebGLuint mGLName; + bool mHasEverBeenBound; + + // we only store pointers to attached renderbuffers, not to attached textures, because + // we will only need to initialize renderbuffers. Textures are already initialized. + Attachment mColorAttachment, + mDepthAttachment, + mStencilAttachment, + mDepthStencilAttachment; +}; + +} // namespace mozilla + +#endif