Bug 1700051: part 26) Correct typo in comment of `mozInlineSpellWordUtil::BuildSoftTe...
[gecko.git] / dom / canvas / WebGLFramebuffer.cpp
blob2feaa22b737e14c566d727f426a70cbb414e82b0
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "WebGLFramebuffer.h"
8 // You know it's going to be fun when these two show up:
9 #include <algorithm>
10 #include <iterator>
12 #include "GLBlitHelper.h"
13 #include "GLContext.h"
14 #include "GLScreenBuffer.h"
15 #include "MozFramebuffer.h"
16 #include "mozilla/dom/WebGLRenderingContextBinding.h"
17 #include "mozilla/IntegerRange.h"
18 #include "nsPrintfCString.h"
19 #include "WebGLContext.h"
20 #include "WebGLContextUtils.h"
21 #include "WebGLExtensions.h"
22 #include "WebGLFormats.h"
23 #include "WebGLRenderbuffer.h"
24 #include "WebGLTexture.h"
26 namespace mozilla {
28 static bool ShouldDeferAttachment(const WebGLContext* const webgl,
29 const GLenum attachPoint) {
30 if (webgl->IsWebGL2()) return false;
32 switch (attachPoint) {
33 case LOCAL_GL_DEPTH_ATTACHMENT:
34 case LOCAL_GL_STENCIL_ATTACHMENT:
35 case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
36 return true;
37 default:
38 return false;
42 WebGLFBAttachPoint::WebGLFBAttachPoint() = default;
43 WebGLFBAttachPoint::WebGLFBAttachPoint(WebGLFBAttachPoint&) = default;
45 WebGLFBAttachPoint::WebGLFBAttachPoint(const WebGLContext* const webgl,
46 const GLenum attachmentPoint)
47 : mAttachmentPoint(attachmentPoint),
48 mDeferAttachment(ShouldDeferAttachment(webgl, mAttachmentPoint)) {}
50 WebGLFBAttachPoint::~WebGLFBAttachPoint() {
51 MOZ_ASSERT(!mRenderbufferPtr);
52 MOZ_ASSERT(!mTexturePtr);
55 void WebGLFBAttachPoint::Clear() { Set(nullptr, {}); }
57 void WebGLFBAttachPoint::Set(gl::GLContext* const gl,
58 const webgl::FbAttachInfo& toAttach) {
59 mRenderbufferPtr = toAttach.rb;
60 mTexturePtr = toAttach.tex;
61 mTexImageLayer = AssertedCast<uint32_t>(toAttach.zLayer);
62 mTexImageZLayerCount = AssertedCast<uint8_t>(toAttach.zLayerCount);
63 mTexImageLevel = AssertedCast<uint8_t>(toAttach.mipLevel);
64 mIsMultiview = toAttach.isMultiview;
66 if (gl && !mDeferAttachment) {
67 DoAttachment(gl);
71 const webgl::ImageInfo* WebGLFBAttachPoint::GetImageInfo() const {
72 if (mTexturePtr) {
73 const auto target = Texture()->Target();
74 uint8_t face = 0;
75 if (target == LOCAL_GL_TEXTURE_CUBE_MAP) {
76 face = Layer() % 6;
78 return &mTexturePtr->ImageInfoAtFace(face, mTexImageLevel);
80 if (mRenderbufferPtr) return &mRenderbufferPtr->ImageInfo();
81 return nullptr;
84 bool WebGLFBAttachPoint::IsComplete(WebGLContext* webgl,
85 nsCString* const out_info) const {
86 MOZ_ASSERT(HasAttachment());
88 const auto fnWriteErrorInfo = [&](const char* const text) {
89 WebGLContext::EnumName(mAttachmentPoint, out_info);
90 out_info->AppendLiteral(": ");
91 out_info->AppendASCII(text);
94 const auto& imageInfo = *GetImageInfo();
95 if (!imageInfo.mWidth || !imageInfo.mHeight) {
96 fnWriteErrorInfo("Attachment has no width or height.");
97 return false;
99 MOZ_ASSERT(imageInfo.IsDefined());
101 const auto& tex = Texture();
102 if (tex) {
103 // ES 3.0 spec, pg 213 has giant blocks of text that bake down to requiring
104 // that attached *non-immutable* tex images are within the valid mip-levels
105 // of the texture. We still need to check immutable textures though, because
106 // checking completeness is also when we zero invalidated/no-data tex
107 // images.
108 const auto attachedMipLevel = MipLevel();
110 const bool withinValidMipLevels = [&]() {
111 const bool ensureInit = false;
112 const auto texCompleteness = tex->CalcCompletenessInfo(ensureInit);
113 if (!texCompleteness) return false; // OOM
115 if (tex->Immutable()) {
116 // Immutable textures can attach a level that's not valid for sampling.
117 // It still has to exist though!
118 return attachedMipLevel < tex->ImmutableLevelCount();
121 // Base level must be complete.
122 if (!texCompleteness->levels) return false;
124 const auto baseLevel = tex->Es3_level_base();
125 if (attachedMipLevel == baseLevel) return true;
127 // If not base level, must be mip-complete and within mips.
128 if (!texCompleteness->mipmapComplete) return false;
129 const auto maxLevel = baseLevel + texCompleteness->levels - 1;
130 return baseLevel <= attachedMipLevel && attachedMipLevel <= maxLevel;
131 }();
132 if (!withinValidMipLevels) {
133 fnWriteErrorInfo("Attached mip level is invalid for texture.");
134 return false;
137 const auto& levelInfo = tex->ImageInfoAtFace(0, attachedMipLevel);
138 const auto faceDepth = levelInfo.mDepth * tex->FaceCount();
139 const bool withinValidZLayers = Layer() + ZLayerCount() - 1 < faceDepth;
140 if (!withinValidZLayers) {
141 fnWriteErrorInfo("Attached z layer is invalid for texture.");
142 return false;
146 const auto& formatUsage = imageInfo.mFormat;
147 if (!formatUsage->IsRenderable()) {
148 const auto info = nsPrintfCString(
149 "Attachment has an effective format of %s,"
150 " which is not renderable.",
151 formatUsage->format->name);
152 fnWriteErrorInfo(info.BeginReading());
153 return false;
155 if (!formatUsage->IsExplicitlyRenderable()) {
156 webgl->WarnIfImplicit(formatUsage->GetExtensionID());
159 const auto format = formatUsage->format;
161 bool hasRequiredBits;
163 switch (mAttachmentPoint) {
164 case LOCAL_GL_DEPTH_ATTACHMENT:
165 hasRequiredBits = format->d;
166 break;
168 case LOCAL_GL_STENCIL_ATTACHMENT:
169 hasRequiredBits = format->s;
170 break;
172 case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
173 MOZ_ASSERT(!webgl->IsWebGL2());
174 hasRequiredBits = (format->d && format->s);
175 break;
177 default:
178 MOZ_ASSERT(mAttachmentPoint >= LOCAL_GL_COLOR_ATTACHMENT0);
179 hasRequiredBits = format->IsColorFormat();
180 break;
183 if (!hasRequiredBits) {
184 fnWriteErrorInfo(
185 "Attachment's format is missing required color/depth/stencil"
186 " bits.");
187 return false;
190 if (!webgl->IsWebGL2()) {
191 bool hasSurplusPlanes = false;
193 switch (mAttachmentPoint) {
194 case LOCAL_GL_DEPTH_ATTACHMENT:
195 hasSurplusPlanes = format->s;
196 break;
198 case LOCAL_GL_STENCIL_ATTACHMENT:
199 hasSurplusPlanes = format->d;
200 break;
203 if (hasSurplusPlanes) {
204 fnWriteErrorInfo(
205 "Attachment has depth or stencil bits when it shouldn't.");
206 return false;
210 return true;
213 void WebGLFBAttachPoint::DoAttachment(gl::GLContext* const gl) const {
214 if (Renderbuffer()) {
215 Renderbuffer()->DoFramebufferRenderbuffer(mAttachmentPoint);
216 return;
219 if (!Texture()) {
220 MOZ_ASSERT(mAttachmentPoint != LOCAL_GL_DEPTH_STENCIL_ATTACHMENT);
221 // WebGL 2 doesn't have a real attachment for this, and WebGL 1 is defered
222 // and only DoAttachment if HasAttachment.
224 gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, mAttachmentPoint,
225 LOCAL_GL_RENDERBUFFER, 0);
226 return;
229 const auto& texName = Texture()->mGLName;
231 switch (Texture()->Target().get()) {
232 case LOCAL_GL_TEXTURE_2D:
233 case LOCAL_GL_TEXTURE_CUBE_MAP: {
234 TexImageTarget imageTarget = LOCAL_GL_TEXTURE_2D;
235 if (Texture()->Target() == LOCAL_GL_TEXTURE_CUBE_MAP) {
236 imageTarget = LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + Layer();
239 if (mAttachmentPoint == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
240 gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
241 LOCAL_GL_DEPTH_ATTACHMENT, imageTarget.get(),
242 texName, MipLevel());
243 gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
244 LOCAL_GL_STENCIL_ATTACHMENT,
245 imageTarget.get(), texName, MipLevel());
246 } else {
247 gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, mAttachmentPoint,
248 imageTarget.get(), texName, MipLevel());
250 break;
253 case LOCAL_GL_TEXTURE_2D_ARRAY:
254 case LOCAL_GL_TEXTURE_3D:
255 if (ZLayerCount() != 1) {
256 gl->fFramebufferTextureMultiview(LOCAL_GL_FRAMEBUFFER, mAttachmentPoint,
257 texName, MipLevel(), Layer(),
258 ZLayerCount());
259 } else {
260 gl->fFramebufferTextureLayer(LOCAL_GL_FRAMEBUFFER, mAttachmentPoint,
261 texName, MipLevel(), Layer());
263 break;
267 Maybe<double> WebGLFBAttachPoint::GetParameter(WebGLContext* webgl,
268 GLenum attachment,
269 GLenum pname) const {
270 if (!HasAttachment()) {
271 // Divergent between GLES 3 and 2.
273 // GLES 2.0.25 p127:
274 // "If the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is NONE, then
275 // querying any other pname will generate INVALID_ENUM."
277 // GLES 3.0.4 p240:
278 // "If the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is NONE, no
279 // framebuffer is bound to target. In this case querying pname
280 // FRAMEBUFFER_ATTACHMENT_OBJECT_NAME will return zero, and all other
281 // queries will generate an INVALID_OPERATION error."
282 switch (pname) {
283 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
284 return Some(LOCAL_GL_NONE);
286 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:
287 if (webgl->IsWebGL2()) return Nothing();
289 break;
291 default:
292 break;
294 nsCString attachmentName;
295 WebGLContext::EnumName(attachment, &attachmentName);
296 if (webgl->IsWebGL2()) {
297 webgl->ErrorInvalidOperation("No attachment at %s.",
298 attachmentName.BeginReading());
299 } else {
300 webgl->ErrorInvalidEnum("No attachment at %s.",
301 attachmentName.BeginReading());
303 return Nothing();
306 bool isPNameValid = false;
307 switch (pname) {
308 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
309 return Some(mTexturePtr ? LOCAL_GL_TEXTURE : LOCAL_GL_RENDERBUFFER);
311 //////
313 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL:
314 if (mTexturePtr) return Some(AssertedCast<uint32_t>(MipLevel()));
315 break;
317 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE:
318 if (mTexturePtr) {
319 GLenum face = 0;
320 if (mTexturePtr->Target() == LOCAL_GL_TEXTURE_CUBE_MAP) {
321 face = LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + Layer();
323 return Some(face);
325 break;
327 //////
329 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER:
330 if (webgl->IsWebGL2()) {
331 return Some(AssertedCast<int32_t>(Layer()));
333 break;
335 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR:
336 if (webgl->IsExtensionEnabled(WebGLExtensionID::OVR_multiview2)) {
337 return Some(AssertedCast<int32_t>(Layer()));
339 break;
341 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR:
342 if (webgl->IsExtensionEnabled(WebGLExtensionID::OVR_multiview2)) {
343 return Some(AssertedCast<uint32_t>(ZLayerCount()));
345 break;
347 //////
349 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE:
350 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE:
351 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE:
352 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE:
353 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE:
354 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE:
355 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE:
356 isPNameValid = webgl->IsWebGL2();
357 break;
359 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING:
360 isPNameValid = (webgl->IsWebGL2() ||
361 webgl->IsExtensionEnabled(WebGLExtensionID::EXT_sRGB));
362 break;
365 if (!isPNameValid) {
366 webgl->ErrorInvalidEnum("Invalid pname: 0x%04x", pname);
367 return Nothing();
370 const auto& imageInfo = *GetImageInfo();
371 const auto& usage = imageInfo.mFormat;
372 if (!usage) {
373 if (pname == LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING)
374 return Some(LOCAL_GL_LINEAR);
376 return Nothing();
379 auto format = usage->format;
381 GLint ret = 0;
382 switch (pname) {
383 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE:
384 ret = format->r;
385 break;
386 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE:
387 ret = format->g;
388 break;
389 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE:
390 ret = format->b;
391 break;
392 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE:
393 ret = format->a;
394 break;
395 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE:
396 ret = format->d;
397 break;
398 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE:
399 ret = format->s;
400 break;
402 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING:
403 ret = (format->isSRGB ? LOCAL_GL_SRGB : LOCAL_GL_LINEAR);
404 break;
406 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE:
407 MOZ_ASSERT(attachment != LOCAL_GL_DEPTH_STENCIL_ATTACHMENT);
409 if (format->unsizedFormat == webgl::UnsizedFormat::DEPTH_STENCIL) {
410 MOZ_ASSERT(attachment == LOCAL_GL_DEPTH_ATTACHMENT ||
411 attachment == LOCAL_GL_STENCIL_ATTACHMENT);
413 if (attachment == LOCAL_GL_DEPTH_ATTACHMENT) {
414 switch (format->effectiveFormat) {
415 case webgl::EffectiveFormat::DEPTH24_STENCIL8:
416 format =
417 webgl::GetFormat(webgl::EffectiveFormat::DEPTH_COMPONENT24);
418 break;
419 case webgl::EffectiveFormat::DEPTH32F_STENCIL8:
420 format =
421 webgl::GetFormat(webgl::EffectiveFormat::DEPTH_COMPONENT32F);
422 break;
423 default:
424 MOZ_ASSERT(false, "no matched DS format");
425 break;
427 } else if (attachment == LOCAL_GL_STENCIL_ATTACHMENT) {
428 switch (format->effectiveFormat) {
429 case webgl::EffectiveFormat::DEPTH24_STENCIL8:
430 case webgl::EffectiveFormat::DEPTH32F_STENCIL8:
431 format = webgl::GetFormat(webgl::EffectiveFormat::STENCIL_INDEX8);
432 break;
433 default:
434 MOZ_ASSERT(false, "no matched DS format");
435 break;
440 switch (format->componentType) {
441 case webgl::ComponentType::Int:
442 ret = LOCAL_GL_INT;
443 break;
444 case webgl::ComponentType::UInt:
445 ret = LOCAL_GL_UNSIGNED_INT;
446 break;
447 case webgl::ComponentType::NormInt:
448 ret = LOCAL_GL_SIGNED_NORMALIZED;
449 break;
450 case webgl::ComponentType::NormUInt:
451 ret = LOCAL_GL_UNSIGNED_NORMALIZED;
452 break;
453 case webgl::ComponentType::Float:
454 ret = LOCAL_GL_FLOAT;
455 break;
457 break;
459 default:
460 MOZ_ASSERT(false, "Missing case.");
461 break;
464 return Some(ret);
467 ////////////////////////////////////////////////////////////////////////////////
468 ////////////////////////////////////////////////////////////////////////////////
469 // WebGLFramebuffer
471 WebGLFramebuffer::WebGLFramebuffer(WebGLContext* webgl, GLuint fbo)
472 : WebGLContextBoundObject(webgl),
473 mGLName(fbo),
474 mDepthAttachment(webgl, LOCAL_GL_DEPTH_ATTACHMENT),
475 mStencilAttachment(webgl, LOCAL_GL_STENCIL_ATTACHMENT),
476 mDepthStencilAttachment(webgl, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
477 mAttachments.push_back(&mDepthAttachment);
478 mAttachments.push_back(&mStencilAttachment);
480 if (!webgl->IsWebGL2()) {
481 // Only WebGL1 has a separate depth+stencil attachment point.
482 mAttachments.push_back(&mDepthStencilAttachment);
485 size_t i = 0;
486 for (auto& cur : mColorAttachments) {
487 new (&cur) WebGLFBAttachPoint(webgl, LOCAL_GL_COLOR_ATTACHMENT0 + i);
488 i++;
490 mAttachments.push_back(&cur);
493 mColorDrawBuffers.push_back(&mColorAttachments[0]);
494 mColorReadBuffer = &mColorAttachments[0];
497 WebGLFramebuffer::WebGLFramebuffer(WebGLContext* webgl,
498 UniquePtr<gl::MozFramebuffer> fbo)
499 : WebGLContextBoundObject(webgl),
500 mGLName(fbo->mFB),
501 mOpaque(std::move(fbo)),
502 mColorReadBuffer(nullptr) {
503 // Opaque Framebuffer is guaranteed to be complete at this point.
504 // Cache the Completeness info.
505 CompletenessInfo info;
506 info.width = mOpaque->mSize.width;
507 info.height = mOpaque->mSize.height;
508 info.hasFloat32 = false;
509 info.zLayerCount = 1;
510 info.isMultiview = false;
512 mCompletenessInfo = Some(std::move(info));
515 WebGLFramebuffer::~WebGLFramebuffer() {
516 InvalidateCaches();
518 mDepthAttachment.Clear();
519 mStencilAttachment.Clear();
520 mDepthStencilAttachment.Clear();
522 for (auto& cur : mColorAttachments) {
523 cur.Clear();
526 if (!mContext) return;
527 // If opaque, fDeleteFramebuffers is called in the destructor of
528 // MozFramebuffer.
529 if (!mOpaque) {
530 mContext->gl->fDeleteFramebuffers(1, &mGLName);
534 ////
536 Maybe<WebGLFBAttachPoint*> WebGLFramebuffer::GetColorAttachPoint(
537 GLenum attachPoint) {
538 if (attachPoint == LOCAL_GL_NONE) return Some<WebGLFBAttachPoint*>(nullptr);
540 if (attachPoint < LOCAL_GL_COLOR_ATTACHMENT0) return Nothing();
542 const size_t colorId = attachPoint - LOCAL_GL_COLOR_ATTACHMENT0;
544 MOZ_ASSERT(mContext->Limits().maxColorDrawBuffers <= kMaxColorAttachments);
545 if (colorId >= mContext->MaxValidDrawBuffers()) return Nothing();
547 return Some(&mColorAttachments[colorId]);
550 Maybe<WebGLFBAttachPoint*> WebGLFramebuffer::GetAttachPoint(
551 GLenum attachPoint) {
552 switch (attachPoint) {
553 case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
554 return Some(&mDepthStencilAttachment);
556 case LOCAL_GL_DEPTH_ATTACHMENT:
557 return Some(&mDepthAttachment);
559 case LOCAL_GL_STENCIL_ATTACHMENT:
560 return Some(&mStencilAttachment);
562 default:
563 return GetColorAttachPoint(attachPoint);
567 void WebGLFramebuffer::DetachTexture(const WebGLTexture* tex) {
568 for (const auto& attach : mAttachments) {
569 if (attach->Texture() == tex) {
570 attach->Clear();
573 InvalidateCaches();
576 void WebGLFramebuffer::DetachRenderbuffer(const WebGLRenderbuffer* rb) {
577 for (const auto& attach : mAttachments) {
578 if (attach->Renderbuffer() == rb) {
579 attach->Clear();
582 InvalidateCaches();
585 ////////////////////////////////////////////////////////////////////////////////
586 // Completeness
588 bool WebGLFramebuffer::HasDuplicateAttachments() const {
589 std::set<WebGLFBAttachPoint::Ordered> uniqueAttachSet;
591 for (const auto& attach : mColorAttachments) {
592 if (!attach.HasAttachment()) continue;
594 const WebGLFBAttachPoint::Ordered ordered(attach);
596 const bool didInsert = uniqueAttachSet.insert(ordered).second;
597 if (!didInsert) return true;
600 return false;
603 bool WebGLFramebuffer::HasDefinedAttachments() const {
604 bool hasAttachments = false;
605 for (const auto& attach : mAttachments) {
606 hasAttachments |= attach->HasAttachment();
608 return hasAttachments;
611 bool WebGLFramebuffer::HasIncompleteAttachments(
612 nsCString* const out_info) const {
613 bool hasIncomplete = false;
614 for (const auto& cur : mAttachments) {
615 if (!cur->HasAttachment())
616 continue; // Not defined, so can't count as incomplete.
618 hasIncomplete |= !cur->IsComplete(mContext, out_info);
620 return hasIncomplete;
623 bool WebGLFramebuffer::AllImageRectsMatch() const {
624 MOZ_ASSERT(HasDefinedAttachments());
625 DebugOnly<nsCString> fbStatusInfo;
626 MOZ_ASSERT(!HasIncompleteAttachments(&fbStatusInfo));
628 bool needsInit = true;
629 uint32_t width = 0;
630 uint32_t height = 0;
632 bool hasMismatch = false;
633 for (const auto& attach : mAttachments) {
634 const auto& imageInfo = attach->GetImageInfo();
635 if (!imageInfo) continue;
637 const auto& curWidth = imageInfo->mWidth;
638 const auto& curHeight = imageInfo->mHeight;
640 if (needsInit) {
641 needsInit = false;
642 width = curWidth;
643 height = curHeight;
644 continue;
647 hasMismatch |= (curWidth != width || curHeight != height);
649 return !hasMismatch;
652 bool WebGLFramebuffer::AllImageSamplesMatch() const {
653 MOZ_ASSERT(HasDefinedAttachments());
654 DebugOnly<nsCString> fbStatusInfo;
655 MOZ_ASSERT(!HasIncompleteAttachments(&fbStatusInfo));
657 bool needsInit = true;
658 uint32_t samples = 0;
660 bool hasMismatch = false;
661 for (const auto& attach : mAttachments) {
662 const auto& imageInfo = attach->GetImageInfo();
663 if (!imageInfo) continue;
665 const auto& curSamples = imageInfo->mSamples;
667 if (needsInit) {
668 needsInit = false;
669 samples = curSamples;
670 continue;
673 hasMismatch |= (curSamples != samples);
675 return !hasMismatch;
678 FBStatus WebGLFramebuffer::PrecheckFramebufferStatus(
679 nsCString* const out_info) const {
680 MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
681 mContext->mBoundReadFramebuffer == this);
682 if (!HasDefinedAttachments())
683 return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; // No
684 // attachments
686 if (HasIncompleteAttachments(out_info))
687 return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
689 if (!AllImageRectsMatch())
690 return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS; // Inconsistent sizes
692 if (!AllImageSamplesMatch())
693 return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE; // Inconsistent samples
695 if (HasDuplicateAttachments()) return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED;
697 if (mContext->IsWebGL2()) {
698 MOZ_ASSERT(!mDepthStencilAttachment.HasAttachment());
699 if (mDepthAttachment.HasAttachment() &&
700 mStencilAttachment.HasAttachment()) {
701 if (!mDepthAttachment.IsEquivalentForFeedback(mStencilAttachment))
702 return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED;
704 } else {
705 const auto depthOrStencilCount =
706 int(mDepthAttachment.HasAttachment()) +
707 int(mStencilAttachment.HasAttachment()) +
708 int(mDepthStencilAttachment.HasAttachment());
709 if (depthOrStencilCount > 1) return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED;
713 const WebGLFBAttachPoint* example = nullptr;
714 for (const auto& x : mAttachments) {
715 if (!x->HasAttachment()) continue;
716 if (!example) {
717 example = x;
718 continue;
720 if (x->ZLayerCount() != example->ZLayerCount()) {
721 return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR;
726 return LOCAL_GL_FRAMEBUFFER_COMPLETE;
729 ////////////////////////////////////////
730 // Validation
732 bool WebGLFramebuffer::ValidateAndInitAttachments(
733 const GLenum incompleteFbError) const {
734 MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
735 mContext->mBoundReadFramebuffer == this);
737 const auto fbStatus = CheckFramebufferStatus();
738 if (fbStatus == LOCAL_GL_FRAMEBUFFER_COMPLETE) return true;
740 mContext->GenerateError(incompleteFbError, "Framebuffer must be complete.");
741 return false;
744 bool WebGLFramebuffer::ValidateClearBufferType(
745 GLenum buffer, uint32_t drawBuffer,
746 const webgl::AttribBaseType funcType) const {
747 if (buffer != LOCAL_GL_COLOR) return true;
749 const auto& attach = mColorAttachments[drawBuffer];
750 const auto& imageInfo = attach.GetImageInfo();
751 if (!imageInfo) return true;
753 if (!count(mColorDrawBuffers.begin(), mColorDrawBuffers.end(), &attach))
754 return true; // DRAW_BUFFERi set to NONE.
756 auto attachType = webgl::AttribBaseType::Float;
757 switch (imageInfo->mFormat->format->componentType) {
758 case webgl::ComponentType::Int:
759 attachType = webgl::AttribBaseType::Int;
760 break;
761 case webgl::ComponentType::UInt:
762 attachType = webgl::AttribBaseType::Uint;
763 break;
764 default:
765 break;
768 if (attachType != funcType) {
769 mContext->ErrorInvalidOperation(
770 "This attachment is of type %s, but"
771 " this function is of type %s.",
772 ToString(attachType), ToString(funcType));
773 return false;
776 return true;
779 bool WebGLFramebuffer::ValidateForColorRead(
780 const webgl::FormatUsageInfo** const out_format, uint32_t* const out_width,
781 uint32_t* const out_height) const {
782 if (!mColorReadBuffer) {
783 mContext->ErrorInvalidOperation("READ_BUFFER must not be NONE.");
784 return false;
787 if (mColorReadBuffer->ZLayerCount() > 1) {
788 mContext->GenerateError(LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION,
789 "The READ_BUFFER attachment has multiple views.");
790 return false;
793 const auto& imageInfo = mColorReadBuffer->GetImageInfo();
794 if (!imageInfo) {
795 mContext->ErrorInvalidOperation(
796 "The READ_BUFFER attachment is not defined.");
797 return false;
800 if (imageInfo->mSamples) {
801 mContext->ErrorInvalidOperation(
802 "The READ_BUFFER attachment is multisampled.");
803 return false;
806 *out_format = imageInfo->mFormat;
807 *out_width = imageInfo->mWidth;
808 *out_height = imageInfo->mHeight;
809 return true;
812 ////////////////////////////////////////////////////////////////////////////////
813 // Resolution and caching
815 void WebGLFramebuffer::DoDeferredAttachments() const {
816 if (mContext->IsWebGL2()) return;
818 const auto& gl = mContext->gl;
819 gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT,
820 LOCAL_GL_RENDERBUFFER, 0);
821 gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
822 LOCAL_GL_STENCIL_ATTACHMENT,
823 LOCAL_GL_RENDERBUFFER, 0);
825 const auto fn = [&](const WebGLFBAttachPoint& attach) {
826 MOZ_ASSERT(attach.mDeferAttachment);
827 if (attach.HasAttachment()) {
828 attach.DoAttachment(gl);
831 // Only one of these will have an attachment.
832 fn(mDepthAttachment);
833 fn(mStencilAttachment);
834 fn(mDepthStencilAttachment);
837 void WebGLFramebuffer::ResolveAttachmentData() const {
838 // GLES 3.0.5 p188:
839 // The result of clearing integer color buffers with `Clear` is undefined.
841 // Two different approaches:
842 // On WebGL 2, we have glClearBuffer, and *must* use it for integer buffers,
843 // so let's just use it for all the buffers. One WebGL 1, we might not have
844 // glClearBuffer,
846 // WebGL 1 is easier, because we can just call glClear, possibly with
847 // glDrawBuffers.
849 const auto& gl = mContext->gl;
851 const webgl::ScopedPrepForResourceClear scopedPrep(*mContext);
853 if (mContext->IsWebGL2()) {
854 const uint32_t uiZeros[4] = {};
855 const int32_t iZeros[4] = {};
856 const float fZeros[4] = {};
857 const float fOne[] = {1.0f};
859 for (const auto& cur : mAttachments) {
860 const auto& imageInfo = cur->GetImageInfo();
861 if (!imageInfo || !imageInfo->mUninitializedSlices)
862 continue; // Nothing attached, or already has data.
864 const auto fnClearBuffer = [&]() {
865 const auto& format = imageInfo->mFormat->format;
866 MOZ_ASSERT(format->estimatedBytesPerPixel <= sizeof(uiZeros));
867 MOZ_ASSERT(format->estimatedBytesPerPixel <= sizeof(iZeros));
868 MOZ_ASSERT(format->estimatedBytesPerPixel <= sizeof(fZeros));
870 switch (cur->mAttachmentPoint) {
871 case LOCAL_GL_DEPTH_ATTACHMENT:
872 gl->fClearBufferfv(LOCAL_GL_DEPTH, 0, fOne);
873 break;
874 case LOCAL_GL_STENCIL_ATTACHMENT:
875 gl->fClearBufferiv(LOCAL_GL_STENCIL, 0, iZeros);
876 break;
877 default:
878 MOZ_ASSERT(cur->mAttachmentPoint !=
879 LOCAL_GL_DEPTH_STENCIL_ATTACHMENT);
880 const uint32_t drawBuffer =
881 cur->mAttachmentPoint - LOCAL_GL_COLOR_ATTACHMENT0;
882 MOZ_ASSERT(drawBuffer <= 100);
883 switch (format->componentType) {
884 case webgl::ComponentType::Int:
885 gl->fClearBufferiv(LOCAL_GL_COLOR, drawBuffer, iZeros);
886 break;
887 case webgl::ComponentType::UInt:
888 gl->fClearBufferuiv(LOCAL_GL_COLOR, drawBuffer, uiZeros);
889 break;
890 default:
891 gl->fClearBufferfv(LOCAL_GL_COLOR, drawBuffer, fZeros);
892 break;
897 if (imageInfo->mDepth > 1) {
898 const auto& tex = cur->Texture();
899 const gl::ScopedFramebuffer scopedFB(gl);
900 const gl::ScopedBindFramebuffer scopedBindFB(gl, scopedFB.FB());
901 for (const auto z : IntegerRange(imageInfo->mDepth)) {
902 if ((*imageInfo->mUninitializedSlices)[z]) {
903 gl->fFramebufferTextureLayer(LOCAL_GL_FRAMEBUFFER,
904 cur->mAttachmentPoint, tex->mGLName,
905 cur->MipLevel(), z);
906 fnClearBuffer();
909 } else {
910 fnClearBuffer();
912 imageInfo->mUninitializedSlices = Nothing();
914 return;
917 uint32_t clearBits = 0;
918 std::vector<GLenum> drawBufferForClear;
920 const auto fnGather = [&](const WebGLFBAttachPoint& attach,
921 const uint32_t attachClearBits) {
922 const auto& imageInfo = attach.GetImageInfo();
923 if (!imageInfo || !imageInfo->mUninitializedSlices) return false;
925 clearBits |= attachClearBits;
926 imageInfo->mUninitializedSlices = Nothing(); // Just mark it now.
927 return true;
930 //////
932 for (const auto& cur : mColorAttachments) {
933 if (fnGather(cur, LOCAL_GL_COLOR_BUFFER_BIT)) {
934 const uint32_t id = cur.mAttachmentPoint - LOCAL_GL_COLOR_ATTACHMENT0;
935 MOZ_ASSERT(id <= 100);
936 drawBufferForClear.resize(id + 1); // Pads with zeros!
937 drawBufferForClear[id] = cur.mAttachmentPoint;
941 (void)fnGather(mDepthAttachment, LOCAL_GL_DEPTH_BUFFER_BIT);
942 (void)fnGather(mStencilAttachment, LOCAL_GL_STENCIL_BUFFER_BIT);
943 (void)fnGather(mDepthStencilAttachment,
944 LOCAL_GL_DEPTH_BUFFER_BIT | LOCAL_GL_STENCIL_BUFFER_BIT);
946 //////
948 if (!clearBits) return;
950 if (gl->IsSupported(gl::GLFeature::draw_buffers)) {
951 gl->fDrawBuffers(drawBufferForClear.size(), drawBufferForClear.data());
954 gl->fClear(clearBits);
956 RefreshDrawBuffers();
959 WebGLFramebuffer::CompletenessInfo::~CompletenessInfo() {
960 if (!this->fb) return;
961 const auto& fb = *this->fb;
962 const auto& webgl = fb.mContext;
963 fb.mNumFBStatusInvals++;
964 if (fb.mNumFBStatusInvals > webgl->mMaxAcceptableFBStatusInvals) {
965 webgl->GeneratePerfWarning(
966 "FB was invalidated after being complete %u"
967 " times. [webgl.perf.max-acceptable-fb-status-invals]",
968 uint32_t(fb.mNumFBStatusInvals));
972 ////////////////////////////////////////////////////////////////////////////////
973 // Entrypoints
975 FBStatus WebGLFramebuffer::CheckFramebufferStatus() const {
976 if (MOZ_UNLIKELY(mOpaque && !mInOpaqueRAF)) {
977 // Opaque Framebuffers are considered incomplete outside of a RAF.
978 return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED;
981 if (mCompletenessInfo) return LOCAL_GL_FRAMEBUFFER_COMPLETE;
983 // Ok, let's try to resolve it!
985 nsCString statusInfo;
986 FBStatus ret = PrecheckFramebufferStatus(&statusInfo);
987 do {
988 if (ret != LOCAL_GL_FRAMEBUFFER_COMPLETE) break;
990 // Looks good on our end. Let's ask the driver.
991 gl::GLContext* const gl = mContext->gl;
993 const ScopedFBRebinder autoFB(mContext);
994 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mGLName);
996 ////
998 DoDeferredAttachments();
999 RefreshDrawBuffers();
1000 RefreshReadBuffer();
1002 ret = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
1004 ////
1006 if (ret != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
1007 const nsPrintfCString text("Bad status according to the driver: 0x%04x",
1008 ret.get());
1009 statusInfo = text;
1010 break;
1013 ResolveAttachmentData();
1015 // Sweet, let's cache that.
1016 auto info = CompletenessInfo{this, UINT32_MAX, UINT32_MAX};
1017 mCompletenessInfo.ResetInvalidators({});
1018 mCompletenessInfo.AddInvalidator(*this);
1020 const auto fnIsFloat32 = [](const webgl::FormatInfo& info) {
1021 if (info.componentType != webgl::ComponentType::Float) return false;
1022 return info.r == 32;
1025 for (const auto& cur : mAttachments) {
1026 const auto& tex = cur->Texture();
1027 const auto& rb = cur->Renderbuffer();
1028 if (tex) {
1029 mCompletenessInfo.AddInvalidator(*tex);
1030 info.texAttachments.push_back(cur);
1031 } else if (rb) {
1032 mCompletenessInfo.AddInvalidator(*rb);
1033 } else {
1034 continue;
1036 const auto& imageInfo = cur->GetImageInfo();
1037 MOZ_ASSERT(imageInfo);
1038 info.width = std::min(info.width, imageInfo->mWidth);
1039 info.height = std::min(info.height, imageInfo->mHeight);
1040 info.hasFloat32 |= fnIsFloat32(*imageInfo->mFormat->format);
1041 info.zLayerCount = cur->ZLayerCount();
1042 info.isMultiview = cur->IsMultiview();
1044 mCompletenessInfo = Some(std::move(info));
1045 info.fb = nullptr; // Don't trigger the invalidation warning.
1046 return LOCAL_GL_FRAMEBUFFER_COMPLETE;
1047 } while (false);
1049 MOZ_ASSERT(ret != LOCAL_GL_FRAMEBUFFER_COMPLETE);
1050 mContext->GenerateWarning("Framebuffer not complete. (status: 0x%04x) %s",
1051 ret.get(), statusInfo.BeginReading());
1052 return ret;
1055 ////
1057 void WebGLFramebuffer::RefreshDrawBuffers() const {
1058 const auto& gl = mContext->gl;
1059 if (!gl->IsSupported(gl::GLFeature::draw_buffers)) return;
1061 // Prior to GL4.1, having a no-image FB attachment that's selected by
1062 // DrawBuffers yields a framebuffer status of
1063 // FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER. We could workaround this only on
1064 // affected versions, but it's easier be unconditional.
1065 std::vector<GLenum> driverBuffers(mContext->Limits().maxColorDrawBuffers,
1066 LOCAL_GL_NONE);
1067 for (const auto& attach : mColorDrawBuffers) {
1068 if (attach->HasAttachment()) {
1069 const uint32_t index =
1070 attach->mAttachmentPoint - LOCAL_GL_COLOR_ATTACHMENT0;
1071 driverBuffers[index] = attach->mAttachmentPoint;
1075 gl->fDrawBuffers(driverBuffers.size(), driverBuffers.data());
1078 void WebGLFramebuffer::RefreshReadBuffer() const {
1079 const auto& gl = mContext->gl;
1080 if (!gl->IsSupported(gl::GLFeature::read_buffer)) return;
1082 // Prior to GL4.1, having a no-image FB attachment that's selected by
1083 // ReadBuffer yields a framebuffer status of
1084 // FRAMEBUFFER_INCOMPLETE_READ_BUFFER. We could workaround this only on
1085 // affected versions, but it's easier be unconditional.
1086 GLenum driverBuffer = LOCAL_GL_NONE;
1087 if (mColorReadBuffer && mColorReadBuffer->HasAttachment()) {
1088 driverBuffer = mColorReadBuffer->mAttachmentPoint;
1091 gl->fReadBuffer(driverBuffer);
1094 ////
1096 void WebGLFramebuffer::DrawBuffers(const std::vector<GLenum>& buffers) {
1097 if (buffers.size() > mContext->MaxValidDrawBuffers()) {
1098 // "An INVALID_VALUE error is generated if `n` is greater than
1099 // MAX_DRAW_BUFFERS."
1100 mContext->ErrorInvalidValue(
1101 "`buffers` must have a length <="
1102 " MAX_DRAW_BUFFERS.");
1103 return;
1106 std::vector<const WebGLFBAttachPoint*> newColorDrawBuffers;
1107 newColorDrawBuffers.reserve(buffers.size());
1109 for (const auto i : IntegerRange(buffers.size())) {
1110 // "If the GL is bound to a draw framebuffer object, the `i`th buffer listed
1111 // in bufs must be COLOR_ATTACHMENTi or NONE. Specifying a buffer out of
1112 // order, BACK, or COLOR_ATTACHMENTm where `m` is greater than or equal to
1113 // the value of MAX_COLOR_ATTACHMENTS, will generate the error
1114 // INVALID_OPERATION.
1116 // WEBGL_draw_buffers:
1117 // "The value of the MAX_COLOR_ATTACHMENTS_WEBGL parameter must be greater
1118 // than or equal to that of the MAX_DRAW_BUFFERS_WEBGL parameter." This
1119 // means that if buffers.Length() isn't larger than MaxDrawBuffers, it won't
1120 // be larger than MaxColorAttachments.
1121 const auto& cur = buffers[i];
1122 if (cur == LOCAL_GL_COLOR_ATTACHMENT0 + i) {
1123 const auto& attach = mColorAttachments[i];
1124 newColorDrawBuffers.push_back(&attach);
1125 } else if (cur != LOCAL_GL_NONE) {
1126 const bool isColorEnum = (cur >= LOCAL_GL_COLOR_ATTACHMENT0 &&
1127 cur < mContext->LastColorAttachmentEnum());
1128 if (cur != LOCAL_GL_BACK && !isColorEnum) {
1129 mContext->ErrorInvalidEnum("Unexpected enum in buffers.");
1130 return;
1133 mContext->ErrorInvalidOperation(
1134 "`buffers[i]` must be NONE or"
1135 " COLOR_ATTACHMENTi.");
1136 return;
1140 ////
1142 mColorDrawBuffers = std::move(newColorDrawBuffers);
1143 RefreshDrawBuffers(); // Calls glDrawBuffers.
1146 bool WebGLFramebuffer::IsDrawBufferEnabled(const uint32_t slotId) const {
1147 const auto attachEnum = LOCAL_GL_COLOR_ATTACHMENT0 + slotId;
1148 for (const auto& cur : mColorDrawBuffers) {
1149 if (cur->mAttachmentPoint == attachEnum) {
1150 return true;
1153 return false;
1156 void WebGLFramebuffer::ReadBuffer(GLenum attachPoint) {
1157 const auto& maybeAttach = GetColorAttachPoint(attachPoint);
1158 if (!maybeAttach) {
1159 const char text[] =
1160 "`mode` must be a COLOR_ATTACHMENTi, for 0 <= i <"
1161 " MAX_DRAW_BUFFERS.";
1162 if (attachPoint == LOCAL_GL_BACK) {
1163 mContext->ErrorInvalidOperation(text);
1164 } else {
1165 mContext->ErrorInvalidEnum(text);
1167 return;
1169 const auto& attach = maybeAttach.value(); // Might be nullptr.
1171 ////
1173 mColorReadBuffer = attach;
1174 RefreshReadBuffer(); // Calls glReadBuffer.
1177 ////
1179 bool WebGLFramebuffer::FramebufferAttach(const GLenum attachEnum,
1180 const webgl::FbAttachInfo& toAttach) {
1181 MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
1182 mContext->mBoundReadFramebuffer == this);
1184 if (MOZ_UNLIKELY(mOpaque)) {
1185 // An opaque framebuffer's attachments cannot be inspected or changed.
1186 return false;
1189 // `attachment`
1190 const auto maybeAttach = GetAttachPoint(attachEnum);
1191 if (!maybeAttach || !maybeAttach.value()) return false;
1192 const auto& attach = maybeAttach.value();
1194 const auto& gl = mContext->gl;
1195 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mGLName);
1196 if (mContext->IsWebGL2() && attachEnum == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
1197 mDepthAttachment.Set(gl, toAttach);
1198 mStencilAttachment.Set(gl, toAttach);
1199 } else {
1200 attach->Set(gl, toAttach);
1202 InvalidateCaches();
1203 return true;
1206 Maybe<double> WebGLFramebuffer::GetAttachmentParameter(GLenum attachEnum,
1207 GLenum pname) {
1208 const auto maybeAttach = GetAttachPoint(attachEnum);
1209 if (!maybeAttach || attachEnum == LOCAL_GL_NONE) {
1210 mContext->ErrorInvalidEnum(
1211 "Can only query COLOR_ATTACHMENTi,"
1212 " DEPTH_ATTACHMENT, DEPTH_STENCIL_ATTACHMENT, or"
1213 " STENCIL_ATTACHMENT for a framebuffer.");
1214 return Nothing();
1216 if (MOZ_UNLIKELY(mOpaque)) {
1217 mContext->ErrorInvalidOperation(
1218 "An opaque framebuffer's attachments cannot be inspected or changed.");
1219 return Nothing();
1221 auto attach = maybeAttach.value();
1223 if (mContext->IsWebGL2() && attachEnum == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
1224 // There are a couple special rules for this one.
1226 if (pname == LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE) {
1227 mContext->ErrorInvalidOperation(
1228 "Querying"
1229 " FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE"
1230 " against DEPTH_STENCIL_ATTACHMENT is an"
1231 " error.");
1232 return Nothing();
1235 if (mDepthAttachment.Renderbuffer() != mStencilAttachment.Renderbuffer() ||
1236 mDepthAttachment.Texture() != mStencilAttachment.Texture()) {
1237 mContext->ErrorInvalidOperation(
1238 "DEPTH_ATTACHMENT and STENCIL_ATTACHMENT"
1239 " have different objects bound.");
1240 return Nothing();
1243 attach = &mDepthAttachment;
1246 return attach->GetParameter(mContext, attachEnum, pname);
1249 ////////////////////
1251 static void GetBackbufferFormats(const WebGLContext* webgl,
1252 const webgl::FormatInfo** const out_color,
1253 const webgl::FormatInfo** const out_depth,
1254 const webgl::FormatInfo** const out_stencil) {
1255 const auto& options = webgl->Options();
1257 const auto effFormat = (options.alpha ? webgl::EffectiveFormat::RGBA8
1258 : webgl::EffectiveFormat::RGB8);
1259 *out_color = webgl::GetFormat(effFormat);
1261 *out_depth = nullptr;
1262 *out_stencil = nullptr;
1263 if (options.depth && options.stencil) {
1264 *out_depth = webgl::GetFormat(webgl::EffectiveFormat::DEPTH24_STENCIL8);
1265 *out_stencil = *out_depth;
1266 } else {
1267 if (options.depth) {
1268 *out_depth = webgl::GetFormat(webgl::EffectiveFormat::DEPTH_COMPONENT16);
1270 if (options.stencil) {
1271 *out_stencil = webgl::GetFormat(webgl::EffectiveFormat::STENCIL_INDEX8);
1276 /*static*/
1277 void WebGLFramebuffer::BlitFramebuffer(WebGLContext* webgl, GLint _srcX0,
1278 GLint _srcY0, GLint _srcX1, GLint _srcY1,
1279 GLint _dstX0, GLint _dstY0, GLint _dstX1,
1280 GLint _dstY1, GLbitfield mask,
1281 GLenum filter) {
1282 auto srcP0 = ivec2{_srcX0, _srcY0};
1283 auto srcP1 = ivec2{_srcX1, _srcY1};
1284 auto dstP0 = ivec2{_dstX0, _dstY0};
1285 auto dstP1 = ivec2{_dstX1, _dstY1};
1287 const GLbitfield depthAndStencilBits =
1288 LOCAL_GL_DEPTH_BUFFER_BIT | LOCAL_GL_STENCIL_BUFFER_BIT;
1289 if (bool(mask & depthAndStencilBits) && filter == LOCAL_GL_LINEAR) {
1290 webgl->ErrorInvalidOperation(
1291 "DEPTH_BUFFER_BIT and STENCIL_BUFFER_BIT can"
1292 " only be used with NEAREST filtering.");
1293 return;
1296 const auto& srcFB = webgl->mBoundReadFramebuffer;
1297 const auto& dstFB = webgl->mBoundDrawFramebuffer;
1299 ////
1300 // Collect data
1302 const auto fnGetFormat =
1303 [](const WebGLFBAttachPoint& cur,
1304 bool* const out_hasSamples) -> const webgl::FormatInfo* {
1305 const auto& imageInfo = cur.GetImageInfo();
1306 if (!imageInfo) return nullptr; // No attachment.
1307 *out_hasSamples = bool(imageInfo->mSamples);
1308 return imageInfo->mFormat->format;
1311 bool srcHasSamples = false;
1312 bool srcIsFilterable = true;
1313 const webgl::FormatInfo* srcColorFormat;
1314 const webgl::FormatInfo* srcDepthFormat;
1315 const webgl::FormatInfo* srcStencilFormat;
1316 gfx::IntSize srcSize;
1318 if (srcFB) {
1319 const auto& info = *srcFB->GetCompletenessInfo();
1320 if (info.zLayerCount != 1) {
1321 webgl->GenerateError(
1322 LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION,
1323 "Source framebuffer cannot have more than one multiview layer.");
1324 return;
1326 srcColorFormat = nullptr;
1327 if (srcFB->mColorReadBuffer) {
1328 const auto& imageInfo = srcFB->mColorReadBuffer->GetImageInfo();
1329 if (imageInfo) {
1330 srcIsFilterable &= imageInfo->mFormat->isFilterable;
1332 srcColorFormat = fnGetFormat(*(srcFB->mColorReadBuffer), &srcHasSamples);
1334 srcDepthFormat = fnGetFormat(srcFB->DepthAttachment(), &srcHasSamples);
1335 srcStencilFormat = fnGetFormat(srcFB->StencilAttachment(), &srcHasSamples);
1336 MOZ_ASSERT(!srcFB->DepthStencilAttachment().HasAttachment());
1337 srcSize = {info.width, info.height};
1338 } else {
1339 srcHasSamples = false; // Always false.
1341 GetBackbufferFormats(webgl, &srcColorFormat, &srcDepthFormat,
1342 &srcStencilFormat);
1343 const auto& size = webgl->DrawingBufferSize();
1344 srcSize = {size.x, size.y};
1347 ////
1349 bool dstHasSamples = false;
1350 const webgl::FormatInfo* dstDepthFormat;
1351 const webgl::FormatInfo* dstStencilFormat;
1352 bool dstHasColor = false;
1353 bool colorFormatsMatch = true;
1354 bool colorTypesMatch = true;
1355 bool colorSrgbMatches = true;
1356 gfx::IntSize dstSize;
1358 const auto fnCheckColorFormat = [&](const webgl::FormatInfo* dstFormat) {
1359 MOZ_ASSERT(dstFormat->r || dstFormat->g || dstFormat->b || dstFormat->a);
1360 dstHasColor = true;
1361 colorFormatsMatch &= (dstFormat == srcColorFormat);
1362 colorTypesMatch &=
1363 srcColorFormat && (dstFormat->baseType == srcColorFormat->baseType);
1364 colorSrgbMatches &=
1365 srcColorFormat && (dstFormat->isSRGB == srcColorFormat->isSRGB);
1368 if (dstFB) {
1369 for (const auto& cur : dstFB->mColorDrawBuffers) {
1370 const auto& format = fnGetFormat(*cur, &dstHasSamples);
1371 if (!format) continue;
1373 fnCheckColorFormat(format);
1376 dstDepthFormat = fnGetFormat(dstFB->DepthAttachment(), &dstHasSamples);
1377 dstStencilFormat = fnGetFormat(dstFB->StencilAttachment(), &dstHasSamples);
1378 MOZ_ASSERT(!dstFB->DepthStencilAttachment().HasAttachment());
1380 const auto& info = *dstFB->GetCompletenessInfo();
1381 if (info.isMultiview) {
1382 webgl->GenerateError(
1383 LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION,
1384 "Destination framebuffer cannot have multiview attachments.");
1385 return;
1387 dstSize = {info.width, info.height};
1388 } else {
1389 dstHasSamples = webgl->Options().antialias;
1391 const webgl::FormatInfo* dstColorFormat;
1392 GetBackbufferFormats(webgl, &dstColorFormat, &dstDepthFormat,
1393 &dstStencilFormat);
1395 fnCheckColorFormat(dstColorFormat);
1397 const auto& size = webgl->DrawingBufferSize();
1398 dstSize = {size.x, size.y};
1401 ////
1402 // Clear unused buffer bits
1404 if (mask & LOCAL_GL_COLOR_BUFFER_BIT && !srcColorFormat && !dstHasColor) {
1405 mask ^= LOCAL_GL_COLOR_BUFFER_BIT;
1408 if (mask & LOCAL_GL_DEPTH_BUFFER_BIT && !srcDepthFormat && !dstDepthFormat) {
1409 mask ^= LOCAL_GL_DEPTH_BUFFER_BIT;
1412 if (mask & LOCAL_GL_STENCIL_BUFFER_BIT && !srcStencilFormat &&
1413 !dstStencilFormat) {
1414 mask ^= LOCAL_GL_STENCIL_BUFFER_BIT;
1417 ////
1418 // Validation
1420 if (dstHasSamples) {
1421 webgl->ErrorInvalidOperation(
1422 "DRAW_FRAMEBUFFER may not have multiple"
1423 " samples.");
1424 return;
1427 bool requireFilterable = (filter == LOCAL_GL_LINEAR);
1428 if (srcHasSamples) {
1429 requireFilterable = false; // It picks one.
1431 if (mask & LOCAL_GL_COLOR_BUFFER_BIT && dstHasColor && !colorFormatsMatch) {
1432 webgl->ErrorInvalidOperation(
1433 "Color buffer formats must match if"
1434 " selected, when reading from a multisampled"
1435 " source.");
1436 return;
1439 if (srcP0 != dstP0 || srcP1 != dstP1) {
1440 webgl->ErrorInvalidOperation(
1441 "If the source is multisampled, then the"
1442 " source and dest regions must match exactly.");
1443 return;
1447 // -
1449 if (mask & LOCAL_GL_COLOR_BUFFER_BIT) {
1450 if (requireFilterable && !srcIsFilterable) {
1451 webgl->ErrorInvalidOperation(
1452 "`filter` is LINEAR and READ_BUFFER"
1453 " contains integer data.");
1454 return;
1457 if (!colorTypesMatch) {
1458 webgl->ErrorInvalidOperation(
1459 "Color component types (float/uint/"
1460 "int) must match.");
1461 return;
1465 /* GLES 3.0.4, p199:
1466 * Calling BlitFramebuffer will result in an INVALID_OPERATION error if
1467 * mask includes DEPTH_BUFFER_BIT or STENCIL_BUFFER_BIT, and the source
1468 * and destination depth and stencil buffer formats do not match.
1470 * jgilbert: The wording is such that if only DEPTH_BUFFER_BIT is specified,
1471 * the stencil formats must match. This seems wrong. It could be a spec bug,
1472 * or I could be missing an interaction in one of the earlier paragraphs.
1474 if (mask & LOCAL_GL_DEPTH_BUFFER_BIT && dstDepthFormat &&
1475 dstDepthFormat != srcDepthFormat) {
1476 webgl->ErrorInvalidOperation(
1477 "Depth buffer formats must match if selected.");
1478 return;
1481 if (mask & LOCAL_GL_STENCIL_BUFFER_BIT && dstStencilFormat &&
1482 dstStencilFormat != srcStencilFormat) {
1483 webgl->ErrorInvalidOperation(
1484 "Stencil buffer formats must match if selected.");
1485 return;
1488 ////
1489 // Check for feedback
1491 if (srcFB && dstFB) {
1492 const WebGLFBAttachPoint* feedback = nullptr;
1494 if (mask & LOCAL_GL_COLOR_BUFFER_BIT) {
1495 MOZ_ASSERT(srcFB->mColorReadBuffer->HasAttachment());
1496 for (const auto& cur : dstFB->mColorDrawBuffers) {
1497 if (srcFB->mColorReadBuffer->IsEquivalentForFeedback(*cur)) {
1498 feedback = cur;
1499 break;
1504 if (mask & LOCAL_GL_DEPTH_BUFFER_BIT &&
1505 srcFB->DepthAttachment().IsEquivalentForFeedback(
1506 dstFB->DepthAttachment())) {
1507 feedback = &dstFB->DepthAttachment();
1510 if (mask & LOCAL_GL_STENCIL_BUFFER_BIT &&
1511 srcFB->StencilAttachment().IsEquivalentForFeedback(
1512 dstFB->StencilAttachment())) {
1513 feedback = &dstFB->StencilAttachment();
1516 if (feedback) {
1517 webgl->ErrorInvalidOperation(
1518 "Feedback detected into DRAW_FRAMEBUFFER's"
1519 " 0x%04x attachment.",
1520 feedback->mAttachmentPoint);
1521 return;
1523 } else if (!srcFB && !dstFB) {
1524 webgl->ErrorInvalidOperation("Feedback with default framebuffer.");
1525 return;
1528 // -
1529 // Mutually constrain src and dst rects for eldritch blits.
1531 [&] {
1532 using fvec2 = avec2<float>; // Switch to float, because there's no perfect
1533 // solution anyway.
1535 const auto zero2f = fvec2{0, 0};
1536 const auto srcSizef = AsVec(srcSize).StaticCast<fvec2>();
1537 const auto dstSizef = AsVec(dstSize).StaticCast<fvec2>();
1539 const auto srcP0f = srcP0.StaticCast<fvec2>();
1540 const auto srcP1f = srcP1.StaticCast<fvec2>();
1541 const auto dstP0f = dstP0.StaticCast<fvec2>();
1542 const auto dstP1f = dstP1.StaticCast<fvec2>();
1544 const auto srcRectDiff = srcP1f - srcP0f;
1545 const auto dstRectDiff = dstP1f - dstP0f;
1547 // Skip if zero-sized.
1548 if (!srcRectDiff.x || !srcRectDiff.y || !dstRectDiff.x || !dstRectDiff.y) {
1549 srcP0 = srcP1 = dstP0 = dstP1 = {0, 0};
1550 return;
1553 // Clamp the rect points
1554 const auto srcQ0 = srcP0f.ClampMinMax(zero2f, srcSizef);
1555 const auto srcQ1 = srcP1f.ClampMinMax(zero2f, srcSizef);
1557 // Normalized to the [0,1] abstact copy rect
1558 const auto srcQ0Norm = (srcQ0 - srcP0f) / srcRectDiff;
1559 const auto srcQ1Norm = (srcQ1 - srcP0f) / srcRectDiff;
1561 // Map into dst
1562 const auto srcQ0InDst = dstP0f + srcQ0Norm * dstRectDiff;
1563 const auto srcQ1InDst = dstP0f + srcQ1Norm * dstRectDiff;
1565 // Clamp the rect points
1566 const auto dstQ0 = srcQ0InDst.ClampMinMax(zero2f, dstSizef);
1567 const auto dstQ1 = srcQ1InDst.ClampMinMax(zero2f, dstSizef);
1569 // Alright, time to go back to src!
1570 // Normalized to the [0,1] abstact copy rect
1571 const auto dstQ0Norm = (dstQ0 - dstP0f) / dstRectDiff;
1572 const auto dstQ1Norm = (dstQ1 - dstP0f) / dstRectDiff;
1574 // Map into src
1575 const auto dstQ0InSrc = srcP0f + dstQ0Norm * srcRectDiff;
1576 const auto dstQ1InSrc = srcP0f + dstQ1Norm * srcRectDiff;
1578 const auto srcQ0Constrained = dstQ0InSrc.ClampMinMax(zero2f, srcSizef);
1579 const auto srcQ1Constrained = dstQ1InSrc.ClampMinMax(zero2f, srcSizef);
1581 // Round, don't floor:
1582 srcP0 = (srcQ0Constrained + 0.5).StaticCast<ivec2>();
1583 srcP1 = (srcQ1Constrained + 0.5).StaticCast<ivec2>();
1584 dstP0 = (dstQ0 + 0.5).StaticCast<ivec2>();
1585 dstP1 = (dstQ1 + 0.5).StaticCast<ivec2>();
1586 }();
1588 bool inBounds = true;
1589 inBounds &= (srcP0 == srcP0.Clamp({0, 0}, AsVec(srcSize)));
1590 inBounds &= (srcP1 == srcP1.Clamp({0, 0}, AsVec(srcSize)));
1591 inBounds &= (dstP0 == dstP0.Clamp({0, 0}, AsVec(dstSize)));
1592 inBounds &= (dstP1 == dstP1.Clamp({0, 0}, AsVec(dstSize)));
1593 if (!inBounds) {
1594 webgl->ErrorImplementationBug(
1595 "Subrects still not within src and dst after constraining.");
1596 return;
1599 // -
1600 // Execute as constrained
1602 const auto& gl = webgl->gl;
1603 const ScopedDrawCallWrapper wrapper(*webgl);
1605 gl->fBlitFramebuffer(srcP0.x, srcP0.y, srcP1.x, srcP1.y, dstP0.x, dstP0.y,
1606 dstP1.x, dstP1.y, mask, filter);
1608 // -
1610 if (mask & LOCAL_GL_COLOR_BUFFER_BIT && !colorSrgbMatches && !gl->IsGLES() &&
1611 gl->Version() < 440) {
1612 // Mostly for Mac.
1613 // Remember, we have to filter in the *linear* format blit.
1615 // src -Blit-> fbB -DrawBlit-> fbC -Blit-> dst
1617 const auto fbB = gl::MozFramebuffer::Create(gl, {1, 1}, 0, false);
1618 const auto fbC = gl::MozFramebuffer::Create(gl, {1, 1}, 0, false);
1620 // -
1622 auto sizeBC = srcSize;
1623 GLenum formatC = LOCAL_GL_RGBA8;
1624 if (srcColorFormat->isSRGB) {
1625 // srgb -> linear
1626 } else {
1627 // linear -> srgb
1628 sizeBC = dstSize;
1629 formatC = LOCAL_GL_SRGB8_ALPHA8;
1632 const auto fnSetTex = [&](const gl::MozFramebuffer& fb,
1633 const GLenum format) {
1634 const gl::ScopedBindTexture bindTex(gl, fb.ColorTex());
1635 gl->fTexStorage2D(LOCAL_GL_TEXTURE_2D, 1, format, sizeBC.width,
1636 sizeBC.height);
1638 fnSetTex(*fbB, srcColorFormat->sizedFormat);
1639 fnSetTex(*fbC, formatC);
1641 // -
1644 const gl::ScopedBindFramebuffer bindFb(gl);
1645 gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, fbB->mFB);
1647 if (srcColorFormat->isSRGB) {
1648 // srgb -> linear
1649 gl->fBlitFramebuffer(srcP0.x, srcP0.y, srcP1.x, srcP1.y, srcP0.x,
1650 srcP0.y, srcP1.x, srcP1.y,
1651 LOCAL_GL_COLOR_BUFFER_BIT, LOCAL_GL_NEAREST);
1652 } else {
1653 // linear -> srgb
1654 gl->fBlitFramebuffer(srcP0.x, srcP0.y, srcP1.x, srcP1.y, dstP0.x,
1655 dstP0.y, dstP1.x, dstP1.y,
1656 LOCAL_GL_COLOR_BUFFER_BIT, filter);
1659 const auto& blitHelper = *gl->BlitHelper();
1660 gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, fbC->mFB);
1661 blitHelper.DrawBlitTextureToFramebuffer(fbB->ColorTex(), sizeBC, sizeBC);
1665 const gl::ScopedBindFramebuffer bindFb(gl);
1666 gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, fbC->mFB);
1668 if (srcColorFormat->isSRGB) {
1669 // srgb -> linear
1670 gl->fBlitFramebuffer(srcP0.x, srcP0.y, srcP1.x, srcP1.y, dstP0.x,
1671 dstP0.y, dstP1.x, dstP1.y,
1672 LOCAL_GL_COLOR_BUFFER_BIT, filter);
1673 } else {
1674 // linear -> srgb
1675 gl->fBlitFramebuffer(dstP0.x, dstP0.y, dstP1.x, dstP1.y, dstP0.x,
1676 dstP0.y, dstP1.x, dstP1.y,
1677 LOCAL_GL_COLOR_BUFFER_BIT, LOCAL_GL_NEAREST);
1682 // -
1683 // glBlitFramebuffer ignores glColorMask!
1685 if (!webgl->mBoundDrawFramebuffer && webgl->mNeedsFakeNoAlpha) {
1686 if (!webgl->mScissorTestEnabled) {
1687 gl->fEnable(LOCAL_GL_SCISSOR_TEST);
1689 if (webgl->mRasterizerDiscardEnabled) {
1690 gl->fDisable(LOCAL_GL_RASTERIZER_DISCARD);
1693 const auto dstRectMin = MinExtents(dstP0, dstP1);
1694 const auto dstRectMax = MaxExtents(dstP0, dstP1);
1695 const auto dstRectSize = dstRectMax - dstRectMin;
1696 const WebGLContext::ScissorRect dstRect = {dstRectMin.x, dstRectMin.y,
1697 dstRectSize.x, dstRectSize.y};
1698 dstRect.Apply(*gl);
1700 gl->fClearColor(0, 0, 0, 1);
1701 webgl->DoColorMask(1 << 3);
1702 gl->fClear(LOCAL_GL_COLOR_BUFFER_BIT);
1704 if (!webgl->mScissorTestEnabled) {
1705 gl->fDisable(LOCAL_GL_SCISSOR_TEST);
1707 if (webgl->mRasterizerDiscardEnabled) {
1708 gl->fEnable(LOCAL_GL_RASTERIZER_DISCARD);
1710 webgl->mScissorRect.Apply(*gl);
1711 gl->fClearColor(webgl->mColorClearValue[0], webgl->mColorClearValue[1],
1712 webgl->mColorClearValue[2], webgl->mColorClearValue[3]);
1716 } // namespace mozilla