michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "WebGLContext.h" michael@0: #include "WebGLFramebuffer.h" michael@0: #include "WebGLExtensions.h" michael@0: #include "WebGLRenderbuffer.h" michael@0: #include "WebGLTexture.h" michael@0: #include "mozilla/dom/WebGLRenderingContextBinding.h" michael@0: #include "WebGLTexture.h" michael@0: #include "WebGLRenderbuffer.h" michael@0: #include "GLContext.h" michael@0: #include "WebGLContextUtils.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::gl; michael@0: michael@0: JSObject* michael@0: WebGLFramebuffer::WrapObject(JSContext* cx) michael@0: { michael@0: return dom::WebGLFramebufferBinding::Wrap(cx, this); michael@0: } michael@0: michael@0: WebGLFramebuffer::WebGLFramebuffer(WebGLContext* context) michael@0: : WebGLContextBoundObject(context) michael@0: , mStatus(0) michael@0: , mHasEverBeenBound(false) michael@0: , mDepthAttachment(LOCAL_GL_DEPTH_ATTACHMENT) michael@0: , mStencilAttachment(LOCAL_GL_STENCIL_ATTACHMENT) michael@0: , mDepthStencilAttachment(LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) michael@0: { michael@0: SetIsDOMBinding(); michael@0: mContext->MakeContextCurrent(); michael@0: mContext->gl->fGenFramebuffers(1, &mGLName); michael@0: mContext->mFramebuffers.insertBack(this); michael@0: michael@0: mColorAttachments.SetLength(1); michael@0: mColorAttachments[0].mAttachmentPoint = LOCAL_GL_COLOR_ATTACHMENT0; michael@0: } michael@0: michael@0: bool michael@0: WebGLFramebuffer::Attachment::IsDeleteRequested() const michael@0: { michael@0: return Texture() ? Texture()->IsDeleteRequested() michael@0: : Renderbuffer() ? Renderbuffer()->IsDeleteRequested() michael@0: : false; michael@0: } michael@0: michael@0: bool michael@0: WebGLFramebuffer::Attachment::HasAlpha() const michael@0: { michael@0: MOZ_ASSERT(HasImage()); michael@0: michael@0: GLenum format = 0; michael@0: if (Texture() && Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel)) michael@0: format = Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).WebGLFormat(); michael@0: else if (Renderbuffer()) michael@0: format = Renderbuffer()->InternalFormat(); michael@0: return FormatHasAlpha(format); michael@0: } michael@0: michael@0: bool michael@0: WebGLFramebuffer::Attachment::IsReadableFloat() const michael@0: { michael@0: if (Texture() && Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel)) { michael@0: GLenum type = Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).WebGLType(); michael@0: switch (type) { michael@0: case LOCAL_GL_FLOAT: michael@0: case LOCAL_GL_HALF_FLOAT_OES: michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: if (Renderbuffer()) { michael@0: GLenum format = Renderbuffer()->InternalFormat(); michael@0: switch (format) { michael@0: case LOCAL_GL_RGB16F: michael@0: case LOCAL_GL_RGBA16F: michael@0: case LOCAL_GL_RGB32F: michael@0: case LOCAL_GL_RGBA32F: michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: MOZ_ASSERT(false, "Should not get here."); michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: WebGLFramebuffer::Attachment::SetTexImage(WebGLTexture* tex, GLenum target, GLint level) michael@0: { michael@0: mTexturePtr = tex; michael@0: mRenderbufferPtr = nullptr; michael@0: mTexImageTarget = target; michael@0: mTexImageLevel = level; michael@0: michael@0: mNeedsFinalize = true; michael@0: } michael@0: michael@0: void michael@0: WebGLFramebuffer::Attachment::SetRenderbuffer(WebGLRenderbuffer* rb) michael@0: { michael@0: mTexturePtr = nullptr; michael@0: mRenderbufferPtr = rb; michael@0: michael@0: mNeedsFinalize = true; michael@0: } michael@0: michael@0: bool michael@0: WebGLFramebuffer::Attachment::HasUninitializedImageData() const michael@0: { michael@0: if (!HasImage()) michael@0: return false; michael@0: michael@0: if (Renderbuffer()) { michael@0: return Renderbuffer()->HasUninitializedImageData(); michael@0: } michael@0: michael@0: if (Texture()) { michael@0: MOZ_ASSERT(Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel)); michael@0: return Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).HasUninitializedImageData(); michael@0: } michael@0: michael@0: MOZ_ASSERT(false, "Should not get here."); michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: WebGLFramebuffer::Attachment::SetImageDataStatus(WebGLImageDataStatus newStatus) michael@0: { michael@0: if (!HasImage()) michael@0: return; michael@0: michael@0: if (Renderbuffer()) { michael@0: Renderbuffer()->SetImageDataStatus(newStatus); michael@0: return; michael@0: } michael@0: michael@0: if (Texture()) { michael@0: Texture()->SetImageDataStatus(mTexImageTarget, mTexImageLevel, newStatus); michael@0: return; michael@0: } michael@0: michael@0: MOZ_ASSERT(false, "Should not get here."); michael@0: } michael@0: michael@0: bool michael@0: WebGLFramebuffer::Attachment::HasImage() const michael@0: { michael@0: if (Texture() && Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel)) michael@0: return true; michael@0: michael@0: if (Renderbuffer()) michael@0: return true; michael@0: michael@0: return false; michael@0: } michael@0: michael@0: const WebGLRectangleObject& michael@0: WebGLFramebuffer::Attachment::RectangleObject() const michael@0: { michael@0: MOZ_ASSERT(HasImage(), "Make sure it has an image before requesting the rectangle."); michael@0: michael@0: if (Texture()) { michael@0: MOZ_ASSERT(Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel)); michael@0: return Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel); michael@0: } michael@0: michael@0: if (Renderbuffer()) { michael@0: return *Renderbuffer(); michael@0: } michael@0: michael@0: MOZ_CRASH("Should not get here."); michael@0: } michael@0: michael@0: /* The following IsValidFBOTextureXXX functions check the internal michael@0: format that is used by GL or GL ES texture formats. This michael@0: corresponds to the state that is stored in michael@0: WebGLTexture::ImageInfo::InternalFormat()*/ michael@0: static inline bool michael@0: IsValidFBOTextureColorFormat(GLenum internalFormat) michael@0: { michael@0: /* These formats are internal formats for each texture -- the actual michael@0: * low level format, which we might have to do conversions for when michael@0: * running against desktop GL (e.g. GL_RGBA + GL_FLOAT -> GL_RGBA32F). michael@0: * michael@0: * This function just handles all of them whether desktop GL or ES. michael@0: */ michael@0: michael@0: return ( michael@0: /* linear 8-bit formats */ michael@0: internalFormat == LOCAL_GL_ALPHA || michael@0: internalFormat == LOCAL_GL_LUMINANCE || michael@0: internalFormat == LOCAL_GL_LUMINANCE_ALPHA || michael@0: internalFormat == LOCAL_GL_RGB || michael@0: internalFormat == LOCAL_GL_RGBA || michael@0: /* sRGB 8-bit formats */ michael@0: internalFormat == LOCAL_GL_SRGB_EXT || michael@0: internalFormat == LOCAL_GL_SRGB_ALPHA_EXT || michael@0: /* linear float32 formats */ michael@0: internalFormat == LOCAL_GL_ALPHA32F_ARB || michael@0: internalFormat == LOCAL_GL_LUMINANCE32F_ARB || michael@0: internalFormat == LOCAL_GL_LUMINANCE_ALPHA32F_ARB || michael@0: internalFormat == LOCAL_GL_RGB32F_ARB || michael@0: internalFormat == LOCAL_GL_RGBA32F_ARB || michael@0: /* texture_half_float formats */ michael@0: internalFormat == LOCAL_GL_ALPHA16F_ARB || michael@0: internalFormat == LOCAL_GL_LUMINANCE16F_ARB || michael@0: internalFormat == LOCAL_GL_LUMINANCE_ALPHA16F_ARB || michael@0: internalFormat == LOCAL_GL_RGB16F_ARB || michael@0: internalFormat == LOCAL_GL_RGBA16F_ARB michael@0: ); michael@0: } michael@0: michael@0: static inline bool michael@0: IsValidFBOTextureDepthFormat(GLenum internalFormat) michael@0: { michael@0: return ( michael@0: internalFormat == LOCAL_GL_DEPTH_COMPONENT || michael@0: internalFormat == LOCAL_GL_DEPTH_COMPONENT16 || michael@0: internalFormat == LOCAL_GL_DEPTH_COMPONENT32); michael@0: } michael@0: michael@0: static inline bool michael@0: IsValidFBOTextureDepthStencilFormat(GLenum internalFormat) michael@0: { michael@0: return ( michael@0: internalFormat == LOCAL_GL_DEPTH_STENCIL || michael@0: internalFormat == LOCAL_GL_DEPTH24_STENCIL8); michael@0: } michael@0: michael@0: /* The following IsValidFBORenderbufferXXX functions check the internal michael@0: format that is stored by WebGLRenderbuffer::InternalFormat(). Valid michael@0: values can be found in WebGLContext::RenderbufferStorage. */ michael@0: static inline bool michael@0: IsValidFBORenderbufferColorFormat(GLenum internalFormat) michael@0: { michael@0: return ( michael@0: internalFormat == LOCAL_GL_RGB565 || michael@0: internalFormat == LOCAL_GL_RGB5_A1 || michael@0: internalFormat == LOCAL_GL_RGBA4 || michael@0: internalFormat == LOCAL_GL_SRGB8_ALPHA8_EXT); michael@0: } michael@0: michael@0: static inline bool michael@0: IsValidFBORenderbufferDepthFormat(GLenum internalFormat) michael@0: { michael@0: return internalFormat == LOCAL_GL_DEPTH_COMPONENT16; michael@0: } michael@0: michael@0: static inline bool michael@0: IsValidFBORenderbufferDepthStencilFormat(GLenum internalFormat) michael@0: { michael@0: return internalFormat == LOCAL_GL_DEPTH_STENCIL; michael@0: } michael@0: michael@0: static inline bool michael@0: IsValidFBORenderbufferStencilFormat(GLenum internalFormat) michael@0: { michael@0: return internalFormat == LOCAL_GL_STENCIL_INDEX8; michael@0: } michael@0: michael@0: bool michael@0: WebGLFramebuffer::Attachment::IsComplete() const michael@0: { michael@0: if (!HasImage()) michael@0: return false; michael@0: michael@0: const WebGLRectangleObject& rect = RectangleObject(); michael@0: michael@0: if (!rect.Width() || michael@0: !rect.Height()) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: if (Texture()) { michael@0: MOZ_ASSERT(Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel)); michael@0: const WebGLTexture::ImageInfo& imageInfo = michael@0: Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel); michael@0: GLenum webGLFormat = imageInfo.WebGLFormat(); michael@0: michael@0: if (mAttachmentPoint == LOCAL_GL_DEPTH_ATTACHMENT) michael@0: return IsValidFBOTextureDepthFormat(webGLFormat); michael@0: michael@0: if (mAttachmentPoint == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) michael@0: return IsValidFBOTextureDepthStencilFormat(webGLFormat); michael@0: michael@0: if (mAttachmentPoint >= LOCAL_GL_COLOR_ATTACHMENT0 && michael@0: mAttachmentPoint < GLenum(LOCAL_GL_COLOR_ATTACHMENT0 + michael@0: WebGLContext::sMaxColorAttachments)) michael@0: { michael@0: return IsValidFBOTextureColorFormat(webGLFormat); michael@0: } michael@0: MOZ_ASSERT(false, "Invalid WebGL attachment point?"); michael@0: return false; michael@0: } michael@0: michael@0: if (Renderbuffer()) { michael@0: GLenum internalFormat = Renderbuffer()->InternalFormat(); michael@0: michael@0: if (mAttachmentPoint == LOCAL_GL_DEPTH_ATTACHMENT) michael@0: return IsValidFBORenderbufferDepthFormat(internalFormat); michael@0: michael@0: if (mAttachmentPoint == LOCAL_GL_STENCIL_ATTACHMENT) michael@0: return IsValidFBORenderbufferStencilFormat(internalFormat); michael@0: michael@0: if (mAttachmentPoint == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) michael@0: return IsValidFBORenderbufferDepthStencilFormat(internalFormat); michael@0: michael@0: if (mAttachmentPoint >= LOCAL_GL_COLOR_ATTACHMENT0 && michael@0: mAttachmentPoint < GLenum(LOCAL_GL_COLOR_ATTACHMENT0 + michael@0: WebGLContext::sMaxColorAttachments)) michael@0: { michael@0: return IsValidFBORenderbufferColorFormat(internalFormat); michael@0: } michael@0: MOZ_ASSERT(false, "Invalid WebGL attachment point?"); michael@0: return false; michael@0: } michael@0: michael@0: MOZ_ASSERT(false, "Should not get here."); michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: WebGLFramebuffer::Attachment::FinalizeAttachment(GLContext* gl, GLenum attachmentLoc) const michael@0: { michael@0: if (!mNeedsFinalize) michael@0: return; michael@0: michael@0: mNeedsFinalize = false; michael@0: michael@0: if (!HasImage()) { michael@0: if (attachmentLoc == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { michael@0: gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT, michael@0: LOCAL_GL_RENDERBUFFER, 0); michael@0: gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT, michael@0: LOCAL_GL_RENDERBUFFER, 0); michael@0: } else { michael@0: gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, attachmentLoc, michael@0: LOCAL_GL_RENDERBUFFER, 0); michael@0: } michael@0: michael@0: return; michael@0: } michael@0: MOZ_ASSERT(HasImage()); michael@0: michael@0: if (Texture()) { michael@0: MOZ_ASSERT(gl == Texture()->Context()->gl); michael@0: michael@0: if (attachmentLoc == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { michael@0: gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT, michael@0: TexImageTarget(), Texture()->GLName(), TexImageLevel()); michael@0: gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT, michael@0: TexImageTarget(), Texture()->GLName(), TexImageLevel()); michael@0: } else { michael@0: gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, attachmentLoc, michael@0: TexImageTarget(), Texture()->GLName(), TexImageLevel()); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: if (Renderbuffer()) { michael@0: Renderbuffer()->FramebufferRenderbuffer(attachmentLoc); michael@0: return; michael@0: } michael@0: michael@0: MOZ_ASSERT(false, "Should not get here."); michael@0: } michael@0: michael@0: void michael@0: WebGLFramebuffer::Delete() michael@0: { michael@0: DetachAllAttachments(); michael@0: mColorAttachments.Clear(); michael@0: mDepthAttachment.Reset(); michael@0: mStencilAttachment.Reset(); michael@0: mDepthStencilAttachment.Reset(); michael@0: michael@0: mContext->MakeContextCurrent(); michael@0: mContext->gl->fDeleteFramebuffers(1, &mGLName); michael@0: LinkedListElement::removeFrom(mContext->mFramebuffers); michael@0: } michael@0: michael@0: void michael@0: WebGLFramebuffer::DetachAttachment(WebGLFramebuffer::Attachment& attachment) michael@0: { michael@0: if (attachment.Texture()) michael@0: attachment.Texture()->DetachFrom(this, attachment.mAttachmentPoint); michael@0: michael@0: if (attachment.Renderbuffer()) michael@0: attachment.Renderbuffer()->DetachFrom(this, attachment.mAttachmentPoint); michael@0: } michael@0: michael@0: void michael@0: WebGLFramebuffer::DetachAllAttachments() michael@0: { michael@0: size_t count = mColorAttachments.Length(); michael@0: for (size_t i = 0; i < count; i++) { michael@0: DetachAttachment(mColorAttachments[i]); michael@0: } michael@0: michael@0: DetachAttachment(mDepthAttachment); michael@0: DetachAttachment(mStencilAttachment); michael@0: DetachAttachment(mDepthStencilAttachment); michael@0: } michael@0: michael@0: void michael@0: WebGLFramebuffer::FramebufferRenderbuffer(GLenum target, michael@0: GLenum attachment, michael@0: GLenum rbtarget, michael@0: WebGLRenderbuffer* wrb) michael@0: { michael@0: MOZ_ASSERT(mContext->mBoundFramebuffer == this); michael@0: michael@0: if (!mContext->ValidateObjectAllowNull("framebufferRenderbuffer: renderbuffer", wrb)) michael@0: return; michael@0: michael@0: if (target != LOCAL_GL_FRAMEBUFFER) michael@0: return mContext->ErrorInvalidEnumInfo("framebufferRenderbuffer: target", target); michael@0: michael@0: if (rbtarget != LOCAL_GL_RENDERBUFFER) michael@0: return mContext->ErrorInvalidEnumInfo("framebufferRenderbuffer: renderbuffer target:", rbtarget); michael@0: michael@0: /* Get the requested attachment. If result is NULL, attachment is michael@0: * invalid and an error is generated. michael@0: * michael@0: * Don't use GetAttachment(...) here because it opt builds it michael@0: * returns mColorAttachment[0] for invalid attachment, which we michael@0: * really don't want to mess with. michael@0: */ michael@0: Attachment* a = GetAttachmentOrNull(attachment); michael@0: if (!a) michael@0: return; // Error generated internally to GetAttachmentOrNull. michael@0: michael@0: /* Invalidate cached framebuffer status and inform texture of it's michael@0: * new attachment michael@0: */ michael@0: mStatus = 0; michael@0: // Detach current michael@0: if (a->Texture()) michael@0: a->Texture()->DetachFrom(this, attachment); michael@0: else if (a->Renderbuffer()) michael@0: a->Renderbuffer()->DetachFrom(this, attachment); michael@0: michael@0: // Attach new michael@0: if (wrb) michael@0: wrb->AttachTo(this, attachment); michael@0: michael@0: a->SetRenderbuffer(wrb); michael@0: } michael@0: michael@0: void michael@0: WebGLFramebuffer::FramebufferTexture2D(GLenum target, michael@0: GLenum attachment, michael@0: GLenum textarget, michael@0: WebGLTexture* wtex, michael@0: GLint level) michael@0: { michael@0: MOZ_ASSERT(mContext->mBoundFramebuffer == this); michael@0: michael@0: if (!mContext->ValidateObjectAllowNull("framebufferTexture2D: texture", wtex)) michael@0: return; michael@0: michael@0: if (target != LOCAL_GL_FRAMEBUFFER) michael@0: return mContext->ErrorInvalidEnumInfo("framebufferTexture2D: target", target); michael@0: michael@0: if (textarget != LOCAL_GL_TEXTURE_2D && michael@0: (textarget < LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X || michael@0: textarget > LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)) michael@0: { michael@0: return mContext->ErrorInvalidEnumInfo("framebufferTexture2D: invalid texture target", textarget); michael@0: } michael@0: michael@0: if (wtex) { michael@0: bool isTexture2D = wtex->Target() == LOCAL_GL_TEXTURE_2D; michael@0: bool isTexTarget2D = textarget == LOCAL_GL_TEXTURE_2D; michael@0: if (isTexture2D != isTexTarget2D) { michael@0: return mContext->ErrorInvalidOperation("framebufferTexture2D: mismatched texture and texture target"); michael@0: } michael@0: } michael@0: michael@0: if (level != 0) michael@0: return mContext->ErrorInvalidValue("framebufferTexture2D: level must be 0"); michael@0: michael@0: /* Get the requested attachment. If result is NULL, attachment is michael@0: * invalid and an error is generated. michael@0: * michael@0: * Don't use GetAttachment(...) here because it opt builds it michael@0: * returns mColorAttachment[0] for invalid attachment, which we michael@0: * really don't want to mess with. michael@0: */ michael@0: Attachment* a = GetAttachmentOrNull(attachment); michael@0: if (!a) michael@0: return; // Error generated internally to GetAttachmentOrNull. michael@0: michael@0: /* Invalidate cached framebuffer status and inform texture of it's michael@0: * new attachment michael@0: */ michael@0: mStatus = 0; michael@0: // Detach current michael@0: if (a->Texture()) michael@0: a->Texture()->DetachFrom(this, attachment); michael@0: else if (a->Renderbuffer()) michael@0: a->Renderbuffer()->DetachFrom(this, attachment); michael@0: michael@0: // Attach new michael@0: if (wtex) michael@0: wtex->AttachTo(this, attachment); michael@0: michael@0: a->SetTexImage(wtex, textarget, level); michael@0: } michael@0: michael@0: WebGLFramebuffer::Attachment* michael@0: WebGLFramebuffer::GetAttachmentOrNull(GLenum attachment) michael@0: { michael@0: if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) michael@0: return &mDepthStencilAttachment; michael@0: michael@0: if (attachment == LOCAL_GL_DEPTH_ATTACHMENT) michael@0: return &mDepthAttachment; michael@0: michael@0: if (attachment == LOCAL_GL_STENCIL_ATTACHMENT) michael@0: return &mStencilAttachment; michael@0: michael@0: if (!CheckColorAttachmentNumber(attachment, "getAttachmentOrNull")) michael@0: return nullptr; michael@0: michael@0: size_t colorAttachmentId = attachment - LOCAL_GL_COLOR_ATTACHMENT0; michael@0: EnsureColorAttachments(colorAttachmentId); michael@0: michael@0: return &mColorAttachments[colorAttachmentId]; michael@0: } michael@0: michael@0: const WebGLFramebuffer::Attachment& michael@0: WebGLFramebuffer::GetAttachment(GLenum attachment) const michael@0: { michael@0: if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) michael@0: return mDepthStencilAttachment; michael@0: if (attachment == LOCAL_GL_DEPTH_ATTACHMENT) michael@0: return mDepthAttachment; michael@0: if (attachment == LOCAL_GL_STENCIL_ATTACHMENT) michael@0: return mStencilAttachment; michael@0: michael@0: if (!CheckColorAttachmentNumber(attachment, "getAttachment")) { michael@0: MOZ_ASSERT(false); michael@0: return mColorAttachments[0]; michael@0: } michael@0: michael@0: size_t colorAttachmentId = attachment - LOCAL_GL_COLOR_ATTACHMENT0; michael@0: if (colorAttachmentId >= mColorAttachments.Length()) { michael@0: MOZ_ASSERT(false); michael@0: return mColorAttachments[0]; michael@0: } michael@0: michael@0: return mColorAttachments[colorAttachmentId]; michael@0: } michael@0: michael@0: void michael@0: WebGLFramebuffer::DetachTexture(const WebGLTexture* tex) michael@0: { michael@0: size_t count = mColorAttachments.Length(); michael@0: for (size_t i = 0; i < count; i++) { michael@0: if (mColorAttachments[i].Texture() == tex) { michael@0: FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0+i, LOCAL_GL_TEXTURE_2D, nullptr, 0); michael@0: // a texture might be attached more that once while editing the framebuffer michael@0: } michael@0: } michael@0: michael@0: if (mDepthAttachment.Texture() == tex) michael@0: FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT, LOCAL_GL_TEXTURE_2D, nullptr, 0); michael@0: if (mStencilAttachment.Texture() == tex) michael@0: FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT, LOCAL_GL_TEXTURE_2D, nullptr, 0); michael@0: if (mDepthStencilAttachment.Texture() == tex) michael@0: FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT, LOCAL_GL_TEXTURE_2D, nullptr, 0); michael@0: } michael@0: michael@0: void michael@0: WebGLFramebuffer::DetachRenderbuffer(const WebGLRenderbuffer* rb) michael@0: { michael@0: size_t count = mColorAttachments.Length(); michael@0: for (size_t i = 0; i < count; i++) { michael@0: if (mColorAttachments[i].Renderbuffer() == rb) { michael@0: FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0+i, LOCAL_GL_RENDERBUFFER, nullptr); michael@0: // a renderbuffer might be attached more that once while editing the framebuffer michael@0: } michael@0: } michael@0: michael@0: if (mDepthAttachment.Renderbuffer() == rb) michael@0: FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT, LOCAL_GL_RENDERBUFFER, nullptr); michael@0: if (mStencilAttachment.Renderbuffer() == rb) michael@0: FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT, LOCAL_GL_RENDERBUFFER, nullptr); michael@0: if (mDepthStencilAttachment.Renderbuffer() == rb) michael@0: FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT, LOCAL_GL_RENDERBUFFER, nullptr); michael@0: } michael@0: michael@0: bool michael@0: WebGLFramebuffer::HasDefinedAttachments() const michael@0: { michael@0: bool hasAttachments = false; michael@0: michael@0: size_t count = mColorAttachments.Length(); michael@0: for (size_t i = 0; i < count; i++) { michael@0: hasAttachments |= mColorAttachments[i].IsDefined(); michael@0: } michael@0: michael@0: hasAttachments |= mDepthAttachment.IsDefined(); michael@0: hasAttachments |= mStencilAttachment.IsDefined(); michael@0: hasAttachments |= mDepthStencilAttachment.IsDefined(); michael@0: michael@0: return hasAttachments; michael@0: } michael@0: michael@0: michael@0: static bool michael@0: IsIncomplete(const WebGLFramebuffer::Attachment& cur) michael@0: { michael@0: return cur.IsDefined() && !cur.IsComplete(); michael@0: } michael@0: michael@0: bool michael@0: WebGLFramebuffer::HasIncompleteAttachments() const michael@0: { michael@0: bool hasIncomplete = false; michael@0: michael@0: size_t count = mColorAttachments.Length(); michael@0: for (size_t i = 0; i < count; i++) { michael@0: hasIncomplete |= IsIncomplete(mColorAttachments[i]); michael@0: } michael@0: michael@0: hasIncomplete |= IsIncomplete(mDepthAttachment); michael@0: hasIncomplete |= IsIncomplete(mStencilAttachment); michael@0: hasIncomplete |= IsIncomplete(mDepthStencilAttachment); michael@0: michael@0: return hasIncomplete; michael@0: } michael@0: michael@0: michael@0: const WebGLRectangleObject& michael@0: WebGLFramebuffer::GetAnyRectObject() const michael@0: { michael@0: MOZ_ASSERT(HasDefinedAttachments()); michael@0: michael@0: size_t count = mColorAttachments.Length(); michael@0: for (size_t i = 0; i < count; i++) { michael@0: if (mColorAttachments[i].HasImage()) michael@0: return mColorAttachments[i].RectangleObject(); michael@0: } michael@0: michael@0: if (mDepthAttachment.HasImage()) michael@0: return mDepthAttachment.RectangleObject(); michael@0: michael@0: if (mStencilAttachment.HasImage()) michael@0: return mStencilAttachment.RectangleObject(); michael@0: michael@0: if (mDepthStencilAttachment.HasImage()) michael@0: return mDepthStencilAttachment.RectangleObject(); michael@0: michael@0: MOZ_CRASH("Should not get here."); michael@0: } michael@0: michael@0: michael@0: static bool michael@0: RectsMatch(const WebGLFramebuffer::Attachment& attachment, michael@0: const WebGLRectangleObject& rect) michael@0: { michael@0: return attachment.RectangleObject().HasSameDimensionsAs(rect); michael@0: } michael@0: michael@0: bool michael@0: WebGLFramebuffer::AllImageRectsMatch() const michael@0: { michael@0: MOZ_ASSERT(HasDefinedAttachments()); michael@0: MOZ_ASSERT(!HasIncompleteAttachments()); michael@0: michael@0: const WebGLRectangleObject& rect = GetAnyRectObject(); michael@0: michael@0: // Alright, we have *a* rect, let's check all the others. michael@0: bool imageRectsMatch = true; michael@0: michael@0: size_t count = mColorAttachments.Length(); michael@0: for (size_t i = 0; i < count; i++) { michael@0: if (mColorAttachments[i].HasImage()) michael@0: imageRectsMatch &= RectsMatch(mColorAttachments[i], rect); michael@0: } michael@0: michael@0: if (mDepthAttachment.HasImage()) michael@0: imageRectsMatch &= RectsMatch(mDepthAttachment, rect); michael@0: michael@0: if (mStencilAttachment.HasImage()) michael@0: imageRectsMatch &= RectsMatch(mStencilAttachment, rect); michael@0: michael@0: if (mDepthStencilAttachment.HasImage()) michael@0: imageRectsMatch &= RectsMatch(mDepthStencilAttachment, rect); michael@0: michael@0: return imageRectsMatch; michael@0: } michael@0: michael@0: michael@0: const WebGLRectangleObject& michael@0: WebGLFramebuffer::RectangleObject() const michael@0: { michael@0: // If we're using this as the RectObj of an FB, we need to be sure the FB michael@0: // has a consistent rect. michael@0: MOZ_ASSERT(AllImageRectsMatch(), "Did you mean `GetAnyRectObject`?"); michael@0: return GetAnyRectObject(); michael@0: } michael@0: michael@0: GLenum michael@0: WebGLFramebuffer::PrecheckFramebufferStatus() const michael@0: { michael@0: MOZ_ASSERT(mContext->mBoundFramebuffer == this); michael@0: michael@0: if (!HasDefinedAttachments()) michael@0: return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; // No attachments michael@0: michael@0: if (HasIncompleteAttachments()) michael@0: return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; michael@0: michael@0: if (!AllImageRectsMatch()) michael@0: return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS; // Inconsistent sizes michael@0: michael@0: if (HasDepthStencilConflict()) michael@0: return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED; michael@0: michael@0: return LOCAL_GL_FRAMEBUFFER_COMPLETE; michael@0: } michael@0: michael@0: GLenum michael@0: WebGLFramebuffer::CheckFramebufferStatus() const michael@0: { michael@0: if (mStatus != 0) michael@0: return mStatus; michael@0: michael@0: mStatus = PrecheckFramebufferStatus(); michael@0: if (mStatus != LOCAL_GL_FRAMEBUFFER_COMPLETE) michael@0: return mStatus; michael@0: michael@0: // Looks good on our end. Let's ask the driver. michael@0: mContext->MakeContextCurrent(); michael@0: michael@0: // Ok, attach our chosen flavor of {DEPTH, STENCIL, DEPTH_STENCIL}. michael@0: FinalizeAttachments(); michael@0: michael@0: mStatus = mContext->gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER); michael@0: return mStatus; michael@0: } michael@0: michael@0: bool michael@0: WebGLFramebuffer::HasCompletePlanes(GLbitfield mask) michael@0: { michael@0: if (CheckFramebufferStatus() != LOCAL_GL_FRAMEBUFFER_COMPLETE) michael@0: return false; michael@0: michael@0: MOZ_ASSERT(mContext->mBoundFramebuffer == this); michael@0: bool hasPlanes = true; michael@0: if (mask & LOCAL_GL_COLOR_BUFFER_BIT) { michael@0: hasPlanes &= ColorAttachmentCount() && michael@0: ColorAttachment(0).IsDefined(); michael@0: } michael@0: michael@0: if (mask & LOCAL_GL_DEPTH_BUFFER_BIT) { michael@0: hasPlanes &= DepthAttachment().IsDefined() || michael@0: DepthStencilAttachment().IsDefined(); michael@0: } michael@0: michael@0: if (mask & LOCAL_GL_STENCIL_BUFFER_BIT) { michael@0: hasPlanes &= StencilAttachment().IsDefined() || michael@0: DepthStencilAttachment().IsDefined(); michael@0: } michael@0: michael@0: return hasPlanes; michael@0: } michael@0: michael@0: bool michael@0: WebGLFramebuffer::CheckAndInitializeAttachments() michael@0: { michael@0: MOZ_ASSERT(mContext->mBoundFramebuffer == this); michael@0: michael@0: if (CheckFramebufferStatus() != LOCAL_GL_FRAMEBUFFER_COMPLETE) michael@0: return false; michael@0: michael@0: // Cool! We've checked out ok. Just need to initialize. michael@0: size_t colorAttachmentCount = mColorAttachments.Length(); michael@0: michael@0: // Check if we need to initialize anything michael@0: { michael@0: bool hasUninitializedAttachments = false; michael@0: michael@0: for (size_t i = 0; i < colorAttachmentCount; i++) { michael@0: if (mColorAttachments[i].HasImage()) michael@0: hasUninitializedAttachments |= mColorAttachments[i].HasUninitializedImageData(); michael@0: } michael@0: michael@0: if (mDepthAttachment.HasImage()) michael@0: hasUninitializedAttachments |= mDepthAttachment.HasUninitializedImageData(); michael@0: if (mStencilAttachment.HasImage()) michael@0: hasUninitializedAttachments |= mStencilAttachment.HasUninitializedImageData(); michael@0: if (mDepthStencilAttachment.HasImage()) michael@0: hasUninitializedAttachments |= mDepthStencilAttachment.HasUninitializedImageData(); michael@0: michael@0: if (!hasUninitializedAttachments) michael@0: return true; michael@0: } michael@0: michael@0: // Get buffer-bit-mask and color-attachment-mask-list michael@0: uint32_t mask = 0; michael@0: bool colorAttachmentsMask[WebGLContext::sMaxColorAttachments] = { false }; michael@0: MOZ_ASSERT(colorAttachmentCount <= WebGLContext::sMaxColorAttachments); michael@0: michael@0: for (size_t i = 0; i < colorAttachmentCount; i++) { michael@0: if (mColorAttachments[i].HasUninitializedImageData()) { michael@0: colorAttachmentsMask[i] = true; michael@0: mask |= LOCAL_GL_COLOR_BUFFER_BIT; michael@0: } michael@0: } michael@0: michael@0: if (mDepthAttachment.HasUninitializedImageData() || michael@0: mDepthStencilAttachment.HasUninitializedImageData()) michael@0: { michael@0: mask |= LOCAL_GL_DEPTH_BUFFER_BIT; michael@0: } michael@0: michael@0: if (mStencilAttachment.HasUninitializedImageData() || michael@0: mDepthStencilAttachment.HasUninitializedImageData()) michael@0: { michael@0: mask |= LOCAL_GL_STENCIL_BUFFER_BIT; michael@0: } michael@0: michael@0: // Clear! michael@0: mContext->ForceClearFramebufferWithDefaultValues(mask, colorAttachmentsMask); michael@0: michael@0: // Mark all the uninitialized images as initialized. michael@0: for (size_t i = 0; i < colorAttachmentCount; i++) { michael@0: if (mColorAttachments[i].HasUninitializedImageData()) michael@0: mColorAttachments[i].SetImageDataStatus(WebGLImageDataStatus::InitializedImageData); michael@0: } michael@0: michael@0: if (mDepthAttachment.HasUninitializedImageData()) michael@0: mDepthAttachment.SetImageDataStatus(WebGLImageDataStatus::InitializedImageData); michael@0: if (mStencilAttachment.HasUninitializedImageData()) michael@0: mStencilAttachment.SetImageDataStatus(WebGLImageDataStatus::InitializedImageData); michael@0: if (mDepthStencilAttachment.HasUninitializedImageData()) michael@0: mDepthStencilAttachment.SetImageDataStatus(WebGLImageDataStatus::InitializedImageData); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool WebGLFramebuffer::CheckColorAttachmentNumber(GLenum attachment, const char* functionName) const michael@0: { michael@0: const char* const errorFormating = "%s: attachment: invalid enum value 0x%x"; michael@0: michael@0: if (mContext->IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers)) { michael@0: if (attachment < LOCAL_GL_COLOR_ATTACHMENT0 || michael@0: attachment >= GLenum(LOCAL_GL_COLOR_ATTACHMENT0 + mContext->mGLMaxColorAttachments)) michael@0: { michael@0: mContext->ErrorInvalidEnum(errorFormating, functionName, attachment); michael@0: return false; michael@0: } michael@0: } else if (attachment != LOCAL_GL_COLOR_ATTACHMENT0) { michael@0: if (attachment > LOCAL_GL_COLOR_ATTACHMENT0 && michael@0: attachment <= LOCAL_GL_COLOR_ATTACHMENT15) michael@0: { michael@0: mContext->ErrorInvalidEnum("%s: attachment: invalid enum value 0x%x. " michael@0: "Try the WEBGL_draw_buffers extension if supported.", functionName, attachment); michael@0: return false; michael@0: } else { michael@0: mContext->ErrorInvalidEnum(errorFormating, functionName, attachment); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void WebGLFramebuffer::EnsureColorAttachments(size_t colorAttachmentId) michael@0: { michael@0: MOZ_ASSERT(colorAttachmentId < WebGLContext::sMaxColorAttachments); michael@0: michael@0: size_t currentAttachmentCount = mColorAttachments.Length(); michael@0: if (colorAttachmentId < currentAttachmentCount) michael@0: return; michael@0: michael@0: mColorAttachments.SetLength(colorAttachmentId + 1); michael@0: michael@0: for (size_t i = colorAttachmentId; i >= currentAttachmentCount; i--) { michael@0: mColorAttachments[i].mAttachmentPoint = LOCAL_GL_COLOR_ATTACHMENT0 + i; michael@0: } michael@0: } michael@0: michael@0: void michael@0: WebGLFramebuffer::NotifyAttachableChanged() const michael@0: { michael@0: // Attachment has changed, so invalidate cached status michael@0: mStatus = 0; michael@0: } michael@0: michael@0: static void michael@0: FinalizeDrawAndReadBuffers(GLContext* aGL, bool aColorBufferDefined) michael@0: { michael@0: MOZ_ASSERT(aGL, "Expected a valid GLContext ptr."); michael@0: // GLES don't support DrawBuffer()/ReadBuffer. michael@0: // According to http://www.opengl.org/wiki/Framebuffer_Object michael@0: // michael@0: // Each draw buffers must either specify color attachment points that have images michael@0: // attached or must be GL_NONE​. (GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER​ when false). michael@0: // michael@0: // If the read buffer is set, then it must specify an attachment point that has an michael@0: // image attached. (GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER​ when false). michael@0: // michael@0: // Note that this test is not performed if OpenGL 4.2 or ARB_ES2_compatibility is michael@0: // available. michael@0: if (aGL->IsGLES() || michael@0: aGL->IsSupported(GLFeature::ES2_compatibility) || michael@0: aGL->IsAtLeast(ContextProfile::OpenGL, 420)) michael@0: { michael@0: return; michael@0: } michael@0: michael@0: // TODO(djg): Assert that fDrawBuffer/fReadBuffer is not NULL. michael@0: GLenum colorBufferSource = aColorBufferDefined ? LOCAL_GL_COLOR_ATTACHMENT0 : LOCAL_GL_NONE; michael@0: aGL->fDrawBuffer(colorBufferSource); michael@0: aGL->fReadBuffer(colorBufferSource); michael@0: } michael@0: michael@0: void michael@0: WebGLFramebuffer::FinalizeAttachments() const michael@0: { michael@0: GLContext* gl = mContext->gl; michael@0: michael@0: size_t count = ColorAttachmentCount(); michael@0: for (size_t i = 0; i < count; i++) { michael@0: ColorAttachment(i).FinalizeAttachment(gl, LOCAL_GL_COLOR_ATTACHMENT0 + i); michael@0: } michael@0: michael@0: DepthAttachment().FinalizeAttachment(gl, LOCAL_GL_DEPTH_ATTACHMENT); michael@0: StencilAttachment().FinalizeAttachment(gl, LOCAL_GL_STENCIL_ATTACHMENT); michael@0: DepthStencilAttachment().FinalizeAttachment(gl, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT); michael@0: michael@0: FinalizeDrawAndReadBuffers(gl, ColorAttachment(0).IsDefined()); michael@0: } michael@0: michael@0: inline void michael@0: ImplCycleCollectionUnlink(mozilla::WebGLFramebuffer::Attachment& aField) michael@0: { michael@0: aField.mTexturePtr = nullptr; michael@0: aField.mRenderbufferPtr = nullptr; michael@0: } michael@0: michael@0: inline void michael@0: ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, michael@0: mozilla::WebGLFramebuffer::Attachment& aField, michael@0: const char* aName, michael@0: uint32_t aFlags = 0) michael@0: { michael@0: CycleCollectionNoteChild(aCallback, aField.mTexturePtr.get(), michael@0: aName, aFlags); michael@0: michael@0: CycleCollectionNoteChild(aCallback, aField.mRenderbufferPtr.get(), michael@0: aName, aFlags); michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_4(WebGLFramebuffer, michael@0: mColorAttachments, michael@0: mDepthAttachment, michael@0: mStencilAttachment, michael@0: mDepthStencilAttachment) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLFramebuffer, AddRef) michael@0: NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLFramebuffer, Release)