1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/canvas/src/WebGLFramebuffer.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,974 @@ 1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "WebGLContext.h" 1.10 +#include "WebGLFramebuffer.h" 1.11 +#include "WebGLExtensions.h" 1.12 +#include "WebGLRenderbuffer.h" 1.13 +#include "WebGLTexture.h" 1.14 +#include "mozilla/dom/WebGLRenderingContextBinding.h" 1.15 +#include "WebGLTexture.h" 1.16 +#include "WebGLRenderbuffer.h" 1.17 +#include "GLContext.h" 1.18 +#include "WebGLContextUtils.h" 1.19 + 1.20 +using namespace mozilla; 1.21 +using namespace mozilla::gl; 1.22 + 1.23 +JSObject* 1.24 +WebGLFramebuffer::WrapObject(JSContext* cx) 1.25 +{ 1.26 + return dom::WebGLFramebufferBinding::Wrap(cx, this); 1.27 +} 1.28 + 1.29 +WebGLFramebuffer::WebGLFramebuffer(WebGLContext* context) 1.30 + : WebGLContextBoundObject(context) 1.31 + , mStatus(0) 1.32 + , mHasEverBeenBound(false) 1.33 + , mDepthAttachment(LOCAL_GL_DEPTH_ATTACHMENT) 1.34 + , mStencilAttachment(LOCAL_GL_STENCIL_ATTACHMENT) 1.35 + , mDepthStencilAttachment(LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) 1.36 +{ 1.37 + SetIsDOMBinding(); 1.38 + mContext->MakeContextCurrent(); 1.39 + mContext->gl->fGenFramebuffers(1, &mGLName); 1.40 + mContext->mFramebuffers.insertBack(this); 1.41 + 1.42 + mColorAttachments.SetLength(1); 1.43 + mColorAttachments[0].mAttachmentPoint = LOCAL_GL_COLOR_ATTACHMENT0; 1.44 +} 1.45 + 1.46 +bool 1.47 +WebGLFramebuffer::Attachment::IsDeleteRequested() const 1.48 +{ 1.49 + return Texture() ? Texture()->IsDeleteRequested() 1.50 + : Renderbuffer() ? Renderbuffer()->IsDeleteRequested() 1.51 + : false; 1.52 +} 1.53 + 1.54 +bool 1.55 +WebGLFramebuffer::Attachment::HasAlpha() const 1.56 +{ 1.57 + MOZ_ASSERT(HasImage()); 1.58 + 1.59 + GLenum format = 0; 1.60 + if (Texture() && Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel)) 1.61 + format = Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).WebGLFormat(); 1.62 + else if (Renderbuffer()) 1.63 + format = Renderbuffer()->InternalFormat(); 1.64 + return FormatHasAlpha(format); 1.65 +} 1.66 + 1.67 +bool 1.68 +WebGLFramebuffer::Attachment::IsReadableFloat() const 1.69 +{ 1.70 + if (Texture() && Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel)) { 1.71 + GLenum type = Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).WebGLType(); 1.72 + switch (type) { 1.73 + case LOCAL_GL_FLOAT: 1.74 + case LOCAL_GL_HALF_FLOAT_OES: 1.75 + return true; 1.76 + } 1.77 + return false; 1.78 + } 1.79 + 1.80 + if (Renderbuffer()) { 1.81 + GLenum format = Renderbuffer()->InternalFormat(); 1.82 + switch (format) { 1.83 + case LOCAL_GL_RGB16F: 1.84 + case LOCAL_GL_RGBA16F: 1.85 + case LOCAL_GL_RGB32F: 1.86 + case LOCAL_GL_RGBA32F: 1.87 + return true; 1.88 + } 1.89 + return false; 1.90 + } 1.91 + 1.92 + MOZ_ASSERT(false, "Should not get here."); 1.93 + return false; 1.94 +} 1.95 + 1.96 +void 1.97 +WebGLFramebuffer::Attachment::SetTexImage(WebGLTexture* tex, GLenum target, GLint level) 1.98 +{ 1.99 + mTexturePtr = tex; 1.100 + mRenderbufferPtr = nullptr; 1.101 + mTexImageTarget = target; 1.102 + mTexImageLevel = level; 1.103 + 1.104 + mNeedsFinalize = true; 1.105 +} 1.106 + 1.107 +void 1.108 +WebGLFramebuffer::Attachment::SetRenderbuffer(WebGLRenderbuffer* rb) 1.109 +{ 1.110 + mTexturePtr = nullptr; 1.111 + mRenderbufferPtr = rb; 1.112 + 1.113 + mNeedsFinalize = true; 1.114 +} 1.115 + 1.116 +bool 1.117 +WebGLFramebuffer::Attachment::HasUninitializedImageData() const 1.118 +{ 1.119 + if (!HasImage()) 1.120 + return false; 1.121 + 1.122 + if (Renderbuffer()) { 1.123 + return Renderbuffer()->HasUninitializedImageData(); 1.124 + } 1.125 + 1.126 + if (Texture()) { 1.127 + MOZ_ASSERT(Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel)); 1.128 + return Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).HasUninitializedImageData(); 1.129 + } 1.130 + 1.131 + MOZ_ASSERT(false, "Should not get here."); 1.132 + return false; 1.133 +} 1.134 + 1.135 +void 1.136 +WebGLFramebuffer::Attachment::SetImageDataStatus(WebGLImageDataStatus newStatus) 1.137 +{ 1.138 + if (!HasImage()) 1.139 + return; 1.140 + 1.141 + if (Renderbuffer()) { 1.142 + Renderbuffer()->SetImageDataStatus(newStatus); 1.143 + return; 1.144 + } 1.145 + 1.146 + if (Texture()) { 1.147 + Texture()->SetImageDataStatus(mTexImageTarget, mTexImageLevel, newStatus); 1.148 + return; 1.149 + } 1.150 + 1.151 + MOZ_ASSERT(false, "Should not get here."); 1.152 +} 1.153 + 1.154 +bool 1.155 +WebGLFramebuffer::Attachment::HasImage() const 1.156 +{ 1.157 + if (Texture() && Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel)) 1.158 + return true; 1.159 + 1.160 + if (Renderbuffer()) 1.161 + return true; 1.162 + 1.163 + return false; 1.164 +} 1.165 + 1.166 +const WebGLRectangleObject& 1.167 +WebGLFramebuffer::Attachment::RectangleObject() const 1.168 +{ 1.169 + MOZ_ASSERT(HasImage(), "Make sure it has an image before requesting the rectangle."); 1.170 + 1.171 + if (Texture()) { 1.172 + MOZ_ASSERT(Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel)); 1.173 + return Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel); 1.174 + } 1.175 + 1.176 + if (Renderbuffer()) { 1.177 + return *Renderbuffer(); 1.178 + } 1.179 + 1.180 + MOZ_CRASH("Should not get here."); 1.181 +} 1.182 + 1.183 +/* The following IsValidFBOTextureXXX functions check the internal 1.184 + format that is used by GL or GL ES texture formats. This 1.185 + corresponds to the state that is stored in 1.186 + WebGLTexture::ImageInfo::InternalFormat()*/ 1.187 +static inline bool 1.188 +IsValidFBOTextureColorFormat(GLenum internalFormat) 1.189 +{ 1.190 + /* These formats are internal formats for each texture -- the actual 1.191 + * low level format, which we might have to do conversions for when 1.192 + * running against desktop GL (e.g. GL_RGBA + GL_FLOAT -> GL_RGBA32F). 1.193 + * 1.194 + * This function just handles all of them whether desktop GL or ES. 1.195 + */ 1.196 + 1.197 + return ( 1.198 + /* linear 8-bit formats */ 1.199 + internalFormat == LOCAL_GL_ALPHA || 1.200 + internalFormat == LOCAL_GL_LUMINANCE || 1.201 + internalFormat == LOCAL_GL_LUMINANCE_ALPHA || 1.202 + internalFormat == LOCAL_GL_RGB || 1.203 + internalFormat == LOCAL_GL_RGBA || 1.204 + /* sRGB 8-bit formats */ 1.205 + internalFormat == LOCAL_GL_SRGB_EXT || 1.206 + internalFormat == LOCAL_GL_SRGB_ALPHA_EXT || 1.207 + /* linear float32 formats */ 1.208 + internalFormat == LOCAL_GL_ALPHA32F_ARB || 1.209 + internalFormat == LOCAL_GL_LUMINANCE32F_ARB || 1.210 + internalFormat == LOCAL_GL_LUMINANCE_ALPHA32F_ARB || 1.211 + internalFormat == LOCAL_GL_RGB32F_ARB || 1.212 + internalFormat == LOCAL_GL_RGBA32F_ARB || 1.213 + /* texture_half_float formats */ 1.214 + internalFormat == LOCAL_GL_ALPHA16F_ARB || 1.215 + internalFormat == LOCAL_GL_LUMINANCE16F_ARB || 1.216 + internalFormat == LOCAL_GL_LUMINANCE_ALPHA16F_ARB || 1.217 + internalFormat == LOCAL_GL_RGB16F_ARB || 1.218 + internalFormat == LOCAL_GL_RGBA16F_ARB 1.219 + ); 1.220 +} 1.221 + 1.222 +static inline bool 1.223 +IsValidFBOTextureDepthFormat(GLenum internalFormat) 1.224 +{ 1.225 + return ( 1.226 + internalFormat == LOCAL_GL_DEPTH_COMPONENT || 1.227 + internalFormat == LOCAL_GL_DEPTH_COMPONENT16 || 1.228 + internalFormat == LOCAL_GL_DEPTH_COMPONENT32); 1.229 +} 1.230 + 1.231 +static inline bool 1.232 +IsValidFBOTextureDepthStencilFormat(GLenum internalFormat) 1.233 +{ 1.234 + return ( 1.235 + internalFormat == LOCAL_GL_DEPTH_STENCIL || 1.236 + internalFormat == LOCAL_GL_DEPTH24_STENCIL8); 1.237 +} 1.238 + 1.239 +/* The following IsValidFBORenderbufferXXX functions check the internal 1.240 + format that is stored by WebGLRenderbuffer::InternalFormat(). Valid 1.241 + values can be found in WebGLContext::RenderbufferStorage. */ 1.242 +static inline bool 1.243 +IsValidFBORenderbufferColorFormat(GLenum internalFormat) 1.244 +{ 1.245 + return ( 1.246 + internalFormat == LOCAL_GL_RGB565 || 1.247 + internalFormat == LOCAL_GL_RGB5_A1 || 1.248 + internalFormat == LOCAL_GL_RGBA4 || 1.249 + internalFormat == LOCAL_GL_SRGB8_ALPHA8_EXT); 1.250 +} 1.251 + 1.252 +static inline bool 1.253 +IsValidFBORenderbufferDepthFormat(GLenum internalFormat) 1.254 +{ 1.255 + return internalFormat == LOCAL_GL_DEPTH_COMPONENT16; 1.256 +} 1.257 + 1.258 +static inline bool 1.259 +IsValidFBORenderbufferDepthStencilFormat(GLenum internalFormat) 1.260 +{ 1.261 + return internalFormat == LOCAL_GL_DEPTH_STENCIL; 1.262 +} 1.263 + 1.264 +static inline bool 1.265 +IsValidFBORenderbufferStencilFormat(GLenum internalFormat) 1.266 +{ 1.267 + return internalFormat == LOCAL_GL_STENCIL_INDEX8; 1.268 +} 1.269 + 1.270 +bool 1.271 +WebGLFramebuffer::Attachment::IsComplete() const 1.272 +{ 1.273 + if (!HasImage()) 1.274 + return false; 1.275 + 1.276 + const WebGLRectangleObject& rect = RectangleObject(); 1.277 + 1.278 + if (!rect.Width() || 1.279 + !rect.Height()) 1.280 + { 1.281 + return false; 1.282 + } 1.283 + 1.284 + if (Texture()) { 1.285 + MOZ_ASSERT(Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel)); 1.286 + const WebGLTexture::ImageInfo& imageInfo = 1.287 + Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel); 1.288 + GLenum webGLFormat = imageInfo.WebGLFormat(); 1.289 + 1.290 + if (mAttachmentPoint == LOCAL_GL_DEPTH_ATTACHMENT) 1.291 + return IsValidFBOTextureDepthFormat(webGLFormat); 1.292 + 1.293 + if (mAttachmentPoint == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) 1.294 + return IsValidFBOTextureDepthStencilFormat(webGLFormat); 1.295 + 1.296 + if (mAttachmentPoint >= LOCAL_GL_COLOR_ATTACHMENT0 && 1.297 + mAttachmentPoint < GLenum(LOCAL_GL_COLOR_ATTACHMENT0 + 1.298 + WebGLContext::sMaxColorAttachments)) 1.299 + { 1.300 + return IsValidFBOTextureColorFormat(webGLFormat); 1.301 + } 1.302 + MOZ_ASSERT(false, "Invalid WebGL attachment point?"); 1.303 + return false; 1.304 + } 1.305 + 1.306 + if (Renderbuffer()) { 1.307 + GLenum internalFormat = Renderbuffer()->InternalFormat(); 1.308 + 1.309 + if (mAttachmentPoint == LOCAL_GL_DEPTH_ATTACHMENT) 1.310 + return IsValidFBORenderbufferDepthFormat(internalFormat); 1.311 + 1.312 + if (mAttachmentPoint == LOCAL_GL_STENCIL_ATTACHMENT) 1.313 + return IsValidFBORenderbufferStencilFormat(internalFormat); 1.314 + 1.315 + if (mAttachmentPoint == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) 1.316 + return IsValidFBORenderbufferDepthStencilFormat(internalFormat); 1.317 + 1.318 + if (mAttachmentPoint >= LOCAL_GL_COLOR_ATTACHMENT0 && 1.319 + mAttachmentPoint < GLenum(LOCAL_GL_COLOR_ATTACHMENT0 + 1.320 + WebGLContext::sMaxColorAttachments)) 1.321 + { 1.322 + return IsValidFBORenderbufferColorFormat(internalFormat); 1.323 + } 1.324 + MOZ_ASSERT(false, "Invalid WebGL attachment point?"); 1.325 + return false; 1.326 + } 1.327 + 1.328 + MOZ_ASSERT(false, "Should not get here."); 1.329 + return false; 1.330 +} 1.331 + 1.332 +void 1.333 +WebGLFramebuffer::Attachment::FinalizeAttachment(GLContext* gl, GLenum attachmentLoc) const 1.334 +{ 1.335 + if (!mNeedsFinalize) 1.336 + return; 1.337 + 1.338 + mNeedsFinalize = false; 1.339 + 1.340 + if (!HasImage()) { 1.341 + if (attachmentLoc == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { 1.342 + gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT, 1.343 + LOCAL_GL_RENDERBUFFER, 0); 1.344 + gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT, 1.345 + LOCAL_GL_RENDERBUFFER, 0); 1.346 + } else { 1.347 + gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, attachmentLoc, 1.348 + LOCAL_GL_RENDERBUFFER, 0); 1.349 + } 1.350 + 1.351 + return; 1.352 + } 1.353 + MOZ_ASSERT(HasImage()); 1.354 + 1.355 + if (Texture()) { 1.356 + MOZ_ASSERT(gl == Texture()->Context()->gl); 1.357 + 1.358 + if (attachmentLoc == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { 1.359 + gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT, 1.360 + TexImageTarget(), Texture()->GLName(), TexImageLevel()); 1.361 + gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT, 1.362 + TexImageTarget(), Texture()->GLName(), TexImageLevel()); 1.363 + } else { 1.364 + gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, attachmentLoc, 1.365 + TexImageTarget(), Texture()->GLName(), TexImageLevel()); 1.366 + } 1.367 + return; 1.368 + } 1.369 + 1.370 + if (Renderbuffer()) { 1.371 + Renderbuffer()->FramebufferRenderbuffer(attachmentLoc); 1.372 + return; 1.373 + } 1.374 + 1.375 + MOZ_ASSERT(false, "Should not get here."); 1.376 +} 1.377 + 1.378 +void 1.379 +WebGLFramebuffer::Delete() 1.380 +{ 1.381 + DetachAllAttachments(); 1.382 + mColorAttachments.Clear(); 1.383 + mDepthAttachment.Reset(); 1.384 + mStencilAttachment.Reset(); 1.385 + mDepthStencilAttachment.Reset(); 1.386 + 1.387 + mContext->MakeContextCurrent(); 1.388 + mContext->gl->fDeleteFramebuffers(1, &mGLName); 1.389 + LinkedListElement<WebGLFramebuffer>::removeFrom(mContext->mFramebuffers); 1.390 +} 1.391 + 1.392 +void 1.393 +WebGLFramebuffer::DetachAttachment(WebGLFramebuffer::Attachment& attachment) 1.394 +{ 1.395 + if (attachment.Texture()) 1.396 + attachment.Texture()->DetachFrom(this, attachment.mAttachmentPoint); 1.397 + 1.398 + if (attachment.Renderbuffer()) 1.399 + attachment.Renderbuffer()->DetachFrom(this, attachment.mAttachmentPoint); 1.400 +} 1.401 + 1.402 +void 1.403 +WebGLFramebuffer::DetachAllAttachments() 1.404 +{ 1.405 + size_t count = mColorAttachments.Length(); 1.406 + for (size_t i = 0; i < count; i++) { 1.407 + DetachAttachment(mColorAttachments[i]); 1.408 + } 1.409 + 1.410 + DetachAttachment(mDepthAttachment); 1.411 + DetachAttachment(mStencilAttachment); 1.412 + DetachAttachment(mDepthStencilAttachment); 1.413 +} 1.414 + 1.415 +void 1.416 +WebGLFramebuffer::FramebufferRenderbuffer(GLenum target, 1.417 + GLenum attachment, 1.418 + GLenum rbtarget, 1.419 + WebGLRenderbuffer* wrb) 1.420 +{ 1.421 + MOZ_ASSERT(mContext->mBoundFramebuffer == this); 1.422 + 1.423 + if (!mContext->ValidateObjectAllowNull("framebufferRenderbuffer: renderbuffer", wrb)) 1.424 + return; 1.425 + 1.426 + if (target != LOCAL_GL_FRAMEBUFFER) 1.427 + return mContext->ErrorInvalidEnumInfo("framebufferRenderbuffer: target", target); 1.428 + 1.429 + if (rbtarget != LOCAL_GL_RENDERBUFFER) 1.430 + return mContext->ErrorInvalidEnumInfo("framebufferRenderbuffer: renderbuffer target:", rbtarget); 1.431 + 1.432 + /* Get the requested attachment. If result is NULL, attachment is 1.433 + * invalid and an error is generated. 1.434 + * 1.435 + * Don't use GetAttachment(...) here because it opt builds it 1.436 + * returns mColorAttachment[0] for invalid attachment, which we 1.437 + * really don't want to mess with. 1.438 + */ 1.439 + Attachment* a = GetAttachmentOrNull(attachment); 1.440 + if (!a) 1.441 + return; // Error generated internally to GetAttachmentOrNull. 1.442 + 1.443 + /* Invalidate cached framebuffer status and inform texture of it's 1.444 + * new attachment 1.445 + */ 1.446 + mStatus = 0; 1.447 + // Detach current 1.448 + if (a->Texture()) 1.449 + a->Texture()->DetachFrom(this, attachment); 1.450 + else if (a->Renderbuffer()) 1.451 + a->Renderbuffer()->DetachFrom(this, attachment); 1.452 + 1.453 + // Attach new 1.454 + if (wrb) 1.455 + wrb->AttachTo(this, attachment); 1.456 + 1.457 + a->SetRenderbuffer(wrb); 1.458 +} 1.459 + 1.460 +void 1.461 +WebGLFramebuffer::FramebufferTexture2D(GLenum target, 1.462 + GLenum attachment, 1.463 + GLenum textarget, 1.464 + WebGLTexture* wtex, 1.465 + GLint level) 1.466 +{ 1.467 + MOZ_ASSERT(mContext->mBoundFramebuffer == this); 1.468 + 1.469 + if (!mContext->ValidateObjectAllowNull("framebufferTexture2D: texture", wtex)) 1.470 + return; 1.471 + 1.472 + if (target != LOCAL_GL_FRAMEBUFFER) 1.473 + return mContext->ErrorInvalidEnumInfo("framebufferTexture2D: target", target); 1.474 + 1.475 + if (textarget != LOCAL_GL_TEXTURE_2D && 1.476 + (textarget < LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X || 1.477 + textarget > LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)) 1.478 + { 1.479 + return mContext->ErrorInvalidEnumInfo("framebufferTexture2D: invalid texture target", textarget); 1.480 + } 1.481 + 1.482 + if (wtex) { 1.483 + bool isTexture2D = wtex->Target() == LOCAL_GL_TEXTURE_2D; 1.484 + bool isTexTarget2D = textarget == LOCAL_GL_TEXTURE_2D; 1.485 + if (isTexture2D != isTexTarget2D) { 1.486 + return mContext->ErrorInvalidOperation("framebufferTexture2D: mismatched texture and texture target"); 1.487 + } 1.488 + } 1.489 + 1.490 + if (level != 0) 1.491 + return mContext->ErrorInvalidValue("framebufferTexture2D: level must be 0"); 1.492 + 1.493 + /* Get the requested attachment. If result is NULL, attachment is 1.494 + * invalid and an error is generated. 1.495 + * 1.496 + * Don't use GetAttachment(...) here because it opt builds it 1.497 + * returns mColorAttachment[0] for invalid attachment, which we 1.498 + * really don't want to mess with. 1.499 + */ 1.500 + Attachment* a = GetAttachmentOrNull(attachment); 1.501 + if (!a) 1.502 + return; // Error generated internally to GetAttachmentOrNull. 1.503 + 1.504 + /* Invalidate cached framebuffer status and inform texture of it's 1.505 + * new attachment 1.506 + */ 1.507 + mStatus = 0; 1.508 + // Detach current 1.509 + if (a->Texture()) 1.510 + a->Texture()->DetachFrom(this, attachment); 1.511 + else if (a->Renderbuffer()) 1.512 + a->Renderbuffer()->DetachFrom(this, attachment); 1.513 + 1.514 + // Attach new 1.515 + if (wtex) 1.516 + wtex->AttachTo(this, attachment); 1.517 + 1.518 + a->SetTexImage(wtex, textarget, level); 1.519 +} 1.520 + 1.521 +WebGLFramebuffer::Attachment* 1.522 +WebGLFramebuffer::GetAttachmentOrNull(GLenum attachment) 1.523 +{ 1.524 + if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) 1.525 + return &mDepthStencilAttachment; 1.526 + 1.527 + if (attachment == LOCAL_GL_DEPTH_ATTACHMENT) 1.528 + return &mDepthAttachment; 1.529 + 1.530 + if (attachment == LOCAL_GL_STENCIL_ATTACHMENT) 1.531 + return &mStencilAttachment; 1.532 + 1.533 + if (!CheckColorAttachmentNumber(attachment, "getAttachmentOrNull")) 1.534 + return nullptr; 1.535 + 1.536 + size_t colorAttachmentId = attachment - LOCAL_GL_COLOR_ATTACHMENT0; 1.537 + EnsureColorAttachments(colorAttachmentId); 1.538 + 1.539 + return &mColorAttachments[colorAttachmentId]; 1.540 +} 1.541 + 1.542 +const WebGLFramebuffer::Attachment& 1.543 +WebGLFramebuffer::GetAttachment(GLenum attachment) const 1.544 +{ 1.545 + if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) 1.546 + return mDepthStencilAttachment; 1.547 + if (attachment == LOCAL_GL_DEPTH_ATTACHMENT) 1.548 + return mDepthAttachment; 1.549 + if (attachment == LOCAL_GL_STENCIL_ATTACHMENT) 1.550 + return mStencilAttachment; 1.551 + 1.552 + if (!CheckColorAttachmentNumber(attachment, "getAttachment")) { 1.553 + MOZ_ASSERT(false); 1.554 + return mColorAttachments[0]; 1.555 + } 1.556 + 1.557 + size_t colorAttachmentId = attachment - LOCAL_GL_COLOR_ATTACHMENT0; 1.558 + if (colorAttachmentId >= mColorAttachments.Length()) { 1.559 + MOZ_ASSERT(false); 1.560 + return mColorAttachments[0]; 1.561 + } 1.562 + 1.563 + return mColorAttachments[colorAttachmentId]; 1.564 +} 1.565 + 1.566 +void 1.567 +WebGLFramebuffer::DetachTexture(const WebGLTexture* tex) 1.568 +{ 1.569 + size_t count = mColorAttachments.Length(); 1.570 + for (size_t i = 0; i < count; i++) { 1.571 + if (mColorAttachments[i].Texture() == tex) { 1.572 + FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0+i, LOCAL_GL_TEXTURE_2D, nullptr, 0); 1.573 + // a texture might be attached more that once while editing the framebuffer 1.574 + } 1.575 + } 1.576 + 1.577 + if (mDepthAttachment.Texture() == tex) 1.578 + FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT, LOCAL_GL_TEXTURE_2D, nullptr, 0); 1.579 + if (mStencilAttachment.Texture() == tex) 1.580 + FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT, LOCAL_GL_TEXTURE_2D, nullptr, 0); 1.581 + if (mDepthStencilAttachment.Texture() == tex) 1.582 + FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT, LOCAL_GL_TEXTURE_2D, nullptr, 0); 1.583 +} 1.584 + 1.585 +void 1.586 +WebGLFramebuffer::DetachRenderbuffer(const WebGLRenderbuffer* rb) 1.587 +{ 1.588 + size_t count = mColorAttachments.Length(); 1.589 + for (size_t i = 0; i < count; i++) { 1.590 + if (mColorAttachments[i].Renderbuffer() == rb) { 1.591 + FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0+i, LOCAL_GL_RENDERBUFFER, nullptr); 1.592 + // a renderbuffer might be attached more that once while editing the framebuffer 1.593 + } 1.594 + } 1.595 + 1.596 + if (mDepthAttachment.Renderbuffer() == rb) 1.597 + FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT, LOCAL_GL_RENDERBUFFER, nullptr); 1.598 + if (mStencilAttachment.Renderbuffer() == rb) 1.599 + FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT, LOCAL_GL_RENDERBUFFER, nullptr); 1.600 + if (mDepthStencilAttachment.Renderbuffer() == rb) 1.601 + FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT, LOCAL_GL_RENDERBUFFER, nullptr); 1.602 +} 1.603 + 1.604 +bool 1.605 +WebGLFramebuffer::HasDefinedAttachments() const 1.606 +{ 1.607 + bool hasAttachments = false; 1.608 + 1.609 + size_t count = mColorAttachments.Length(); 1.610 + for (size_t i = 0; i < count; i++) { 1.611 + hasAttachments |= mColorAttachments[i].IsDefined(); 1.612 + } 1.613 + 1.614 + hasAttachments |= mDepthAttachment.IsDefined(); 1.615 + hasAttachments |= mStencilAttachment.IsDefined(); 1.616 + hasAttachments |= mDepthStencilAttachment.IsDefined(); 1.617 + 1.618 + return hasAttachments; 1.619 +} 1.620 + 1.621 + 1.622 +static bool 1.623 +IsIncomplete(const WebGLFramebuffer::Attachment& cur) 1.624 +{ 1.625 + return cur.IsDefined() && !cur.IsComplete(); 1.626 +} 1.627 + 1.628 +bool 1.629 +WebGLFramebuffer::HasIncompleteAttachments() const 1.630 +{ 1.631 + bool hasIncomplete = false; 1.632 + 1.633 + size_t count = mColorAttachments.Length(); 1.634 + for (size_t i = 0; i < count; i++) { 1.635 + hasIncomplete |= IsIncomplete(mColorAttachments[i]); 1.636 + } 1.637 + 1.638 + hasIncomplete |= IsIncomplete(mDepthAttachment); 1.639 + hasIncomplete |= IsIncomplete(mStencilAttachment); 1.640 + hasIncomplete |= IsIncomplete(mDepthStencilAttachment); 1.641 + 1.642 + return hasIncomplete; 1.643 +} 1.644 + 1.645 + 1.646 +const WebGLRectangleObject& 1.647 +WebGLFramebuffer::GetAnyRectObject() const 1.648 +{ 1.649 + MOZ_ASSERT(HasDefinedAttachments()); 1.650 + 1.651 + size_t count = mColorAttachments.Length(); 1.652 + for (size_t i = 0; i < count; i++) { 1.653 + if (mColorAttachments[i].HasImage()) 1.654 + return mColorAttachments[i].RectangleObject(); 1.655 + } 1.656 + 1.657 + if (mDepthAttachment.HasImage()) 1.658 + return mDepthAttachment.RectangleObject(); 1.659 + 1.660 + if (mStencilAttachment.HasImage()) 1.661 + return mStencilAttachment.RectangleObject(); 1.662 + 1.663 + if (mDepthStencilAttachment.HasImage()) 1.664 + return mDepthStencilAttachment.RectangleObject(); 1.665 + 1.666 + MOZ_CRASH("Should not get here."); 1.667 +} 1.668 + 1.669 + 1.670 +static bool 1.671 +RectsMatch(const WebGLFramebuffer::Attachment& attachment, 1.672 + const WebGLRectangleObject& rect) 1.673 +{ 1.674 + return attachment.RectangleObject().HasSameDimensionsAs(rect); 1.675 +} 1.676 + 1.677 +bool 1.678 +WebGLFramebuffer::AllImageRectsMatch() const 1.679 +{ 1.680 + MOZ_ASSERT(HasDefinedAttachments()); 1.681 + MOZ_ASSERT(!HasIncompleteAttachments()); 1.682 + 1.683 + const WebGLRectangleObject& rect = GetAnyRectObject(); 1.684 + 1.685 + // Alright, we have *a* rect, let's check all the others. 1.686 + bool imageRectsMatch = true; 1.687 + 1.688 + size_t count = mColorAttachments.Length(); 1.689 + for (size_t i = 0; i < count; i++) { 1.690 + if (mColorAttachments[i].HasImage()) 1.691 + imageRectsMatch &= RectsMatch(mColorAttachments[i], rect); 1.692 + } 1.693 + 1.694 + if (mDepthAttachment.HasImage()) 1.695 + imageRectsMatch &= RectsMatch(mDepthAttachment, rect); 1.696 + 1.697 + if (mStencilAttachment.HasImage()) 1.698 + imageRectsMatch &= RectsMatch(mStencilAttachment, rect); 1.699 + 1.700 + if (mDepthStencilAttachment.HasImage()) 1.701 + imageRectsMatch &= RectsMatch(mDepthStencilAttachment, rect); 1.702 + 1.703 + return imageRectsMatch; 1.704 +} 1.705 + 1.706 + 1.707 +const WebGLRectangleObject& 1.708 +WebGLFramebuffer::RectangleObject() const 1.709 +{ 1.710 + // If we're using this as the RectObj of an FB, we need to be sure the FB 1.711 + // has a consistent rect. 1.712 + MOZ_ASSERT(AllImageRectsMatch(), "Did you mean `GetAnyRectObject`?"); 1.713 + return GetAnyRectObject(); 1.714 +} 1.715 + 1.716 +GLenum 1.717 +WebGLFramebuffer::PrecheckFramebufferStatus() const 1.718 +{ 1.719 + MOZ_ASSERT(mContext->mBoundFramebuffer == this); 1.720 + 1.721 + if (!HasDefinedAttachments()) 1.722 + return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; // No attachments 1.723 + 1.724 + if (HasIncompleteAttachments()) 1.725 + return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; 1.726 + 1.727 + if (!AllImageRectsMatch()) 1.728 + return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS; // Inconsistent sizes 1.729 + 1.730 + if (HasDepthStencilConflict()) 1.731 + return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED; 1.732 + 1.733 + return LOCAL_GL_FRAMEBUFFER_COMPLETE; 1.734 +} 1.735 + 1.736 +GLenum 1.737 +WebGLFramebuffer::CheckFramebufferStatus() const 1.738 +{ 1.739 + if (mStatus != 0) 1.740 + return mStatus; 1.741 + 1.742 + mStatus = PrecheckFramebufferStatus(); 1.743 + if (mStatus != LOCAL_GL_FRAMEBUFFER_COMPLETE) 1.744 + return mStatus; 1.745 + 1.746 + // Looks good on our end. Let's ask the driver. 1.747 + mContext->MakeContextCurrent(); 1.748 + 1.749 + // Ok, attach our chosen flavor of {DEPTH, STENCIL, DEPTH_STENCIL}. 1.750 + FinalizeAttachments(); 1.751 + 1.752 + mStatus = mContext->gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER); 1.753 + return mStatus; 1.754 +} 1.755 + 1.756 +bool 1.757 +WebGLFramebuffer::HasCompletePlanes(GLbitfield mask) 1.758 +{ 1.759 + if (CheckFramebufferStatus() != LOCAL_GL_FRAMEBUFFER_COMPLETE) 1.760 + return false; 1.761 + 1.762 + MOZ_ASSERT(mContext->mBoundFramebuffer == this); 1.763 + bool hasPlanes = true; 1.764 + if (mask & LOCAL_GL_COLOR_BUFFER_BIT) { 1.765 + hasPlanes &= ColorAttachmentCount() && 1.766 + ColorAttachment(0).IsDefined(); 1.767 + } 1.768 + 1.769 + if (mask & LOCAL_GL_DEPTH_BUFFER_BIT) { 1.770 + hasPlanes &= DepthAttachment().IsDefined() || 1.771 + DepthStencilAttachment().IsDefined(); 1.772 + } 1.773 + 1.774 + if (mask & LOCAL_GL_STENCIL_BUFFER_BIT) { 1.775 + hasPlanes &= StencilAttachment().IsDefined() || 1.776 + DepthStencilAttachment().IsDefined(); 1.777 + } 1.778 + 1.779 + return hasPlanes; 1.780 +} 1.781 + 1.782 +bool 1.783 +WebGLFramebuffer::CheckAndInitializeAttachments() 1.784 +{ 1.785 + MOZ_ASSERT(mContext->mBoundFramebuffer == this); 1.786 + 1.787 + if (CheckFramebufferStatus() != LOCAL_GL_FRAMEBUFFER_COMPLETE) 1.788 + return false; 1.789 + 1.790 + // Cool! We've checked out ok. Just need to initialize. 1.791 + size_t colorAttachmentCount = mColorAttachments.Length(); 1.792 + 1.793 + // Check if we need to initialize anything 1.794 + { 1.795 + bool hasUninitializedAttachments = false; 1.796 + 1.797 + for (size_t i = 0; i < colorAttachmentCount; i++) { 1.798 + if (mColorAttachments[i].HasImage()) 1.799 + hasUninitializedAttachments |= mColorAttachments[i].HasUninitializedImageData(); 1.800 + } 1.801 + 1.802 + if (mDepthAttachment.HasImage()) 1.803 + hasUninitializedAttachments |= mDepthAttachment.HasUninitializedImageData(); 1.804 + if (mStencilAttachment.HasImage()) 1.805 + hasUninitializedAttachments |= mStencilAttachment.HasUninitializedImageData(); 1.806 + if (mDepthStencilAttachment.HasImage()) 1.807 + hasUninitializedAttachments |= mDepthStencilAttachment.HasUninitializedImageData(); 1.808 + 1.809 + if (!hasUninitializedAttachments) 1.810 + return true; 1.811 + } 1.812 + 1.813 + // Get buffer-bit-mask and color-attachment-mask-list 1.814 + uint32_t mask = 0; 1.815 + bool colorAttachmentsMask[WebGLContext::sMaxColorAttachments] = { false }; 1.816 + MOZ_ASSERT(colorAttachmentCount <= WebGLContext::sMaxColorAttachments); 1.817 + 1.818 + for (size_t i = 0; i < colorAttachmentCount; i++) { 1.819 + if (mColorAttachments[i].HasUninitializedImageData()) { 1.820 + colorAttachmentsMask[i] = true; 1.821 + mask |= LOCAL_GL_COLOR_BUFFER_BIT; 1.822 + } 1.823 + } 1.824 + 1.825 + if (mDepthAttachment.HasUninitializedImageData() || 1.826 + mDepthStencilAttachment.HasUninitializedImageData()) 1.827 + { 1.828 + mask |= LOCAL_GL_DEPTH_BUFFER_BIT; 1.829 + } 1.830 + 1.831 + if (mStencilAttachment.HasUninitializedImageData() || 1.832 + mDepthStencilAttachment.HasUninitializedImageData()) 1.833 + { 1.834 + mask |= LOCAL_GL_STENCIL_BUFFER_BIT; 1.835 + } 1.836 + 1.837 + // Clear! 1.838 + mContext->ForceClearFramebufferWithDefaultValues(mask, colorAttachmentsMask); 1.839 + 1.840 + // Mark all the uninitialized images as initialized. 1.841 + for (size_t i = 0; i < colorAttachmentCount; i++) { 1.842 + if (mColorAttachments[i].HasUninitializedImageData()) 1.843 + mColorAttachments[i].SetImageDataStatus(WebGLImageDataStatus::InitializedImageData); 1.844 + } 1.845 + 1.846 + if (mDepthAttachment.HasUninitializedImageData()) 1.847 + mDepthAttachment.SetImageDataStatus(WebGLImageDataStatus::InitializedImageData); 1.848 + if (mStencilAttachment.HasUninitializedImageData()) 1.849 + mStencilAttachment.SetImageDataStatus(WebGLImageDataStatus::InitializedImageData); 1.850 + if (mDepthStencilAttachment.HasUninitializedImageData()) 1.851 + mDepthStencilAttachment.SetImageDataStatus(WebGLImageDataStatus::InitializedImageData); 1.852 + 1.853 + return true; 1.854 +} 1.855 + 1.856 +bool WebGLFramebuffer::CheckColorAttachmentNumber(GLenum attachment, const char* functionName) const 1.857 +{ 1.858 + const char* const errorFormating = "%s: attachment: invalid enum value 0x%x"; 1.859 + 1.860 + if (mContext->IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers)) { 1.861 + if (attachment < LOCAL_GL_COLOR_ATTACHMENT0 || 1.862 + attachment >= GLenum(LOCAL_GL_COLOR_ATTACHMENT0 + mContext->mGLMaxColorAttachments)) 1.863 + { 1.864 + mContext->ErrorInvalidEnum(errorFormating, functionName, attachment); 1.865 + return false; 1.866 + } 1.867 + } else if (attachment != LOCAL_GL_COLOR_ATTACHMENT0) { 1.868 + if (attachment > LOCAL_GL_COLOR_ATTACHMENT0 && 1.869 + attachment <= LOCAL_GL_COLOR_ATTACHMENT15) 1.870 + { 1.871 + mContext->ErrorInvalidEnum("%s: attachment: invalid enum value 0x%x. " 1.872 + "Try the WEBGL_draw_buffers extension if supported.", functionName, attachment); 1.873 + return false; 1.874 + } else { 1.875 + mContext->ErrorInvalidEnum(errorFormating, functionName, attachment); 1.876 + return false; 1.877 + } 1.878 + } 1.879 + 1.880 + return true; 1.881 +} 1.882 + 1.883 +void WebGLFramebuffer::EnsureColorAttachments(size_t colorAttachmentId) 1.884 +{ 1.885 + MOZ_ASSERT(colorAttachmentId < WebGLContext::sMaxColorAttachments); 1.886 + 1.887 + size_t currentAttachmentCount = mColorAttachments.Length(); 1.888 + if (colorAttachmentId < currentAttachmentCount) 1.889 + return; 1.890 + 1.891 + mColorAttachments.SetLength(colorAttachmentId + 1); 1.892 + 1.893 + for (size_t i = colorAttachmentId; i >= currentAttachmentCount; i--) { 1.894 + mColorAttachments[i].mAttachmentPoint = LOCAL_GL_COLOR_ATTACHMENT0 + i; 1.895 + } 1.896 +} 1.897 + 1.898 +void 1.899 +WebGLFramebuffer::NotifyAttachableChanged() const 1.900 +{ 1.901 + // Attachment has changed, so invalidate cached status 1.902 + mStatus = 0; 1.903 +} 1.904 + 1.905 +static void 1.906 +FinalizeDrawAndReadBuffers(GLContext* aGL, bool aColorBufferDefined) 1.907 +{ 1.908 + MOZ_ASSERT(aGL, "Expected a valid GLContext ptr."); 1.909 + // GLES don't support DrawBuffer()/ReadBuffer. 1.910 + // According to http://www.opengl.org/wiki/Framebuffer_Object 1.911 + // 1.912 + // Each draw buffers must either specify color attachment points that have images 1.913 + // attached or must be GL_NONE. (GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER when false). 1.914 + // 1.915 + // If the read buffer is set, then it must specify an attachment point that has an 1.916 + // image attached. (GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER when false). 1.917 + // 1.918 + // Note that this test is not performed if OpenGL 4.2 or ARB_ES2_compatibility is 1.919 + // available. 1.920 + if (aGL->IsGLES() || 1.921 + aGL->IsSupported(GLFeature::ES2_compatibility) || 1.922 + aGL->IsAtLeast(ContextProfile::OpenGL, 420)) 1.923 + { 1.924 + return; 1.925 + } 1.926 + 1.927 + // TODO(djg): Assert that fDrawBuffer/fReadBuffer is not NULL. 1.928 + GLenum colorBufferSource = aColorBufferDefined ? LOCAL_GL_COLOR_ATTACHMENT0 : LOCAL_GL_NONE; 1.929 + aGL->fDrawBuffer(colorBufferSource); 1.930 + aGL->fReadBuffer(colorBufferSource); 1.931 +} 1.932 + 1.933 +void 1.934 +WebGLFramebuffer::FinalizeAttachments() const 1.935 +{ 1.936 + GLContext* gl = mContext->gl; 1.937 + 1.938 + size_t count = ColorAttachmentCount(); 1.939 + for (size_t i = 0; i < count; i++) { 1.940 + ColorAttachment(i).FinalizeAttachment(gl, LOCAL_GL_COLOR_ATTACHMENT0 + i); 1.941 + } 1.942 + 1.943 + DepthAttachment().FinalizeAttachment(gl, LOCAL_GL_DEPTH_ATTACHMENT); 1.944 + StencilAttachment().FinalizeAttachment(gl, LOCAL_GL_STENCIL_ATTACHMENT); 1.945 + DepthStencilAttachment().FinalizeAttachment(gl, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT); 1.946 + 1.947 + FinalizeDrawAndReadBuffers(gl, ColorAttachment(0).IsDefined()); 1.948 +} 1.949 + 1.950 +inline void 1.951 +ImplCycleCollectionUnlink(mozilla::WebGLFramebuffer::Attachment& aField) 1.952 +{ 1.953 + aField.mTexturePtr = nullptr; 1.954 + aField.mRenderbufferPtr = nullptr; 1.955 +} 1.956 + 1.957 +inline void 1.958 +ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, 1.959 + mozilla::WebGLFramebuffer::Attachment& aField, 1.960 + const char* aName, 1.961 + uint32_t aFlags = 0) 1.962 +{ 1.963 + CycleCollectionNoteChild(aCallback, aField.mTexturePtr.get(), 1.964 + aName, aFlags); 1.965 + 1.966 + CycleCollectionNoteChild(aCallback, aField.mRenderbufferPtr.get(), 1.967 + aName, aFlags); 1.968 +} 1.969 + 1.970 +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_4(WebGLFramebuffer, 1.971 + mColorAttachments, 1.972 + mDepthAttachment, 1.973 + mStencilAttachment, 1.974 + mDepthStencilAttachment) 1.975 + 1.976 +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLFramebuffer, AddRef) 1.977 +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLFramebuffer, Release)