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/. */
5 
6 #include "WebGLFramebuffer.h"
7 
8 // You know it's going to be fun when these two show up:
9 #include <algorithm>
10 #include <iterator>
11 
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"
25 
26 namespace mozilla {
27 
ShouldDeferAttachment(const WebGLContext * const webgl,const GLenum attachPoint)28 static bool ShouldDeferAttachment(const WebGLContext* const webgl,
29                                   const GLenum attachPoint) {
30   if (webgl->IsWebGL2()) return false;
31 
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;
39   }
40 }
41 
42 WebGLFBAttachPoint::WebGLFBAttachPoint() = default;
43 WebGLFBAttachPoint::WebGLFBAttachPoint(WebGLFBAttachPoint&) = default;
44 
WebGLFBAttachPoint(const WebGLContext * const webgl,const GLenum attachmentPoint)45 WebGLFBAttachPoint::WebGLFBAttachPoint(const WebGLContext* const webgl,
46                                        const GLenum attachmentPoint)
47     : mAttachmentPoint(attachmentPoint),
48       mDeferAttachment(ShouldDeferAttachment(webgl, mAttachmentPoint)) {}
49 
~WebGLFBAttachPoint()50 WebGLFBAttachPoint::~WebGLFBAttachPoint() {
51   MOZ_ASSERT(!mRenderbufferPtr);
52   MOZ_ASSERT(!mTexturePtr);
53 }
54 
Clear()55 void WebGLFBAttachPoint::Clear() { Set(nullptr, {}); }
56 
Set(gl::GLContext * const gl,const webgl::FbAttachInfo & toAttach)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;
65 
66   if (gl && !mDeferAttachment) {
67     DoAttachment(gl);
68   }
69 }
70 
GetImageInfo() const71 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;
77     }
78     return &mTexturePtr->ImageInfoAtFace(face, mTexImageLevel);
79   }
80   if (mRenderbufferPtr) return &mRenderbufferPtr->ImageInfo();
81   return nullptr;
82 }
83 
IsComplete(WebGLContext * webgl,nsCString * const out_info) const84 bool WebGLFBAttachPoint::IsComplete(WebGLContext* webgl,
85                                     nsCString* const out_info) const {
86   MOZ_ASSERT(HasAttachment());
87 
88   const auto fnWriteErrorInfo = [&](const char* const text) {
89     WebGLContext::EnumName(mAttachmentPoint, out_info);
90     out_info->AppendLiteral(": ");
91     out_info->AppendASCII(text);
92   };
93 
94   const auto& imageInfo = *GetImageInfo();
95   if (!imageInfo.mWidth || !imageInfo.mHeight) {
96     fnWriteErrorInfo("Attachment has no width or height.");
97     return false;
98   }
99   MOZ_ASSERT(imageInfo.IsDefined());
100 
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();
109 
110     const bool withinValidMipLevels = [&]() {
111       const bool ensureInit = false;
112       const auto texCompleteness = tex->CalcCompletenessInfo(ensureInit);
113       if (!texCompleteness) return false;  // OOM
114 
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();
119       }
120 
121       // Base level must be complete.
122       if (!texCompleteness->levels) return false;
123 
124       const auto baseLevel = tex->Es3_level_base();
125       if (attachedMipLevel == baseLevel) return true;
126 
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;
135     }
136 
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;
143     }
144   }
145 
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;
154   }
155   if (!formatUsage->IsExplicitlyRenderable()) {
156     webgl->WarnIfImplicit(formatUsage->GetExtensionID());
157   }
158 
159   const auto format = formatUsage->format;
160 
161   bool hasRequiredBits;
162 
163   switch (mAttachmentPoint) {
164     case LOCAL_GL_DEPTH_ATTACHMENT:
165       hasRequiredBits = format->d;
166       break;
167 
168     case LOCAL_GL_STENCIL_ATTACHMENT:
169       hasRequiredBits = format->s;
170       break;
171 
172     case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
173       MOZ_ASSERT(!webgl->IsWebGL2());
174       hasRequiredBits = (format->d && format->s);
175       break;
176 
177     default:
178       MOZ_ASSERT(mAttachmentPoint >= LOCAL_GL_COLOR_ATTACHMENT0);
179       hasRequiredBits = format->IsColorFormat();
180       break;
181   }
182 
183   if (!hasRequiredBits) {
184     fnWriteErrorInfo(
185         "Attachment's format is missing required color/depth/stencil"
186         " bits.");
187     return false;
188   }
189 
190   if (!webgl->IsWebGL2()) {
191     bool hasSurplusPlanes = false;
192 
193     switch (mAttachmentPoint) {
194       case LOCAL_GL_DEPTH_ATTACHMENT:
195         hasSurplusPlanes = format->s;
196         break;
197 
198       case LOCAL_GL_STENCIL_ATTACHMENT:
199         hasSurplusPlanes = format->d;
200         break;
201     }
202 
203     if (hasSurplusPlanes) {
204       fnWriteErrorInfo(
205           "Attachment has depth or stencil bits when it shouldn't.");
206       return false;
207     }
208   }
209 
210   return true;
211 }
212 
DoAttachment(gl::GLContext * const gl) const213 void WebGLFBAttachPoint::DoAttachment(gl::GLContext* const gl) const {
214   if (Renderbuffer()) {
215     Renderbuffer()->DoFramebufferRenderbuffer(mAttachmentPoint);
216     return;
217   }
218 
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.
223 
224     gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, mAttachmentPoint,
225                                  LOCAL_GL_RENDERBUFFER, 0);
226     return;
227   }
228 
229   const auto& texName = Texture()->mGLName;
230 
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();
237       }
238 
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());
249       }
250       break;
251     }
252 
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());
262       }
263       break;
264   }
265 }
266 
GetParameter(WebGLContext * webgl,GLenum attachment,GLenum pname) const267 Maybe<double> WebGLFBAttachPoint::GetParameter(WebGLContext* webgl,
268                                                GLenum attachment,
269                                                GLenum pname) const {
270   if (!HasAttachment()) {
271     // Divergent between GLES 3 and 2.
272 
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."
276 
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);
285 
286       case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:
287         if (webgl->IsWebGL2()) return Nothing();
288 
289         break;
290 
291       default:
292         break;
293     }
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());
302     }
303     return Nothing();
304   }
305 
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);
310 
311       //////
312 
313     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL:
314       if (mTexturePtr) return Some(AssertedCast<uint32_t>(MipLevel()));
315       break;
316 
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();
322         }
323         return Some(face);
324       }
325       break;
326 
327       //////
328 
329     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER:
330       if (webgl->IsWebGL2()) {
331         return Some(AssertedCast<int32_t>(Layer()));
332       }
333       break;
334 
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()));
338       }
339       break;
340 
341     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR:
342       if (webgl->IsExtensionEnabled(WebGLExtensionID::OVR_multiview2)) {
343         return Some(AssertedCast<uint32_t>(ZLayerCount()));
344       }
345       break;
346 
347       //////
348 
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;
358 
359     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING:
360       isPNameValid = (webgl->IsWebGL2() ||
361                       webgl->IsExtensionEnabled(WebGLExtensionID::EXT_sRGB));
362       break;
363   }
364 
365   if (!isPNameValid) {
366     webgl->ErrorInvalidEnum("Invalid pname: 0x%04x", pname);
367     return Nothing();
368   }
369 
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);
375 
376     return Nothing();
377   }
378 
379   auto format = usage->format;
380 
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;
401 
402     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING:
403       ret = (format->isSRGB ? LOCAL_GL_SRGB : LOCAL_GL_LINEAR);
404       break;
405 
406     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE:
407       MOZ_ASSERT(attachment != LOCAL_GL_DEPTH_STENCIL_ATTACHMENT);
408 
409       if (format->unsizedFormat == webgl::UnsizedFormat::DEPTH_STENCIL) {
410         MOZ_ASSERT(attachment == LOCAL_GL_DEPTH_ATTACHMENT ||
411                    attachment == LOCAL_GL_STENCIL_ATTACHMENT);
412 
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;
426           }
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;
436           }
437         }
438       }
439 
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;
456       }
457       break;
458 
459     default:
460       MOZ_ASSERT(false, "Missing case.");
461       break;
462   }
463 
464   return Some(ret);
465 }
466 
467 ////////////////////////////////////////////////////////////////////////////////
468 ////////////////////////////////////////////////////////////////////////////////
469 // WebGLFramebuffer
470 
WebGLFramebuffer(WebGLContext * webgl,GLuint fbo)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);
479 
480   if (!webgl->IsWebGL2()) {
481     // Only WebGL1 has a separate depth+stencil attachment point.
482     mAttachments.push_back(&mDepthStencilAttachment);
483   }
484 
485   size_t i = 0;
486   for (auto& cur : mColorAttachments) {
487     new (&cur) WebGLFBAttachPoint(webgl, LOCAL_GL_COLOR_ATTACHMENT0 + i);
488     i++;
489 
490     mAttachments.push_back(&cur);
491   }
492 
493   mColorDrawBuffers.push_back(&mColorAttachments[0]);
494   mColorReadBuffer = &mColorAttachments[0];
495 }
496 
WebGLFramebuffer(WebGLContext * webgl,UniquePtr<gl::MozFramebuffer> fbo)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.zLayerCount = 1;
509   info.isMultiview = false;
510 
511   mCompletenessInfo = Some(std::move(info));
512 }
513 
~WebGLFramebuffer()514 WebGLFramebuffer::~WebGLFramebuffer() {
515   InvalidateCaches();
516 
517   mDepthAttachment.Clear();
518   mStencilAttachment.Clear();
519   mDepthStencilAttachment.Clear();
520 
521   for (auto& cur : mColorAttachments) {
522     cur.Clear();
523   }
524 
525   if (!mContext) return;
526   // If opaque, fDeleteFramebuffers is called in the destructor of
527   // MozFramebuffer.
528   if (!mOpaque) {
529     mContext->gl->fDeleteFramebuffers(1, &mGLName);
530   }
531 }
532 
533 ////
534 
GetColorAttachPoint(GLenum attachPoint)535 Maybe<WebGLFBAttachPoint*> WebGLFramebuffer::GetColorAttachPoint(
536     GLenum attachPoint) {
537   if (attachPoint == LOCAL_GL_NONE) return Some<WebGLFBAttachPoint*>(nullptr);
538 
539   if (attachPoint < LOCAL_GL_COLOR_ATTACHMENT0) return Nothing();
540 
541   const size_t colorId = attachPoint - LOCAL_GL_COLOR_ATTACHMENT0;
542 
543   MOZ_ASSERT(mContext->Limits().maxColorDrawBuffers <= webgl::kMaxDrawBuffers);
544   if (colorId >= mContext->MaxValidDrawBuffers()) return Nothing();
545 
546   return Some(&mColorAttachments[colorId]);
547 }
548 
GetAttachPoint(GLenum attachPoint)549 Maybe<WebGLFBAttachPoint*> WebGLFramebuffer::GetAttachPoint(
550     GLenum attachPoint) {
551   switch (attachPoint) {
552     case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
553       return Some(&mDepthStencilAttachment);
554 
555     case LOCAL_GL_DEPTH_ATTACHMENT:
556       return Some(&mDepthAttachment);
557 
558     case LOCAL_GL_STENCIL_ATTACHMENT:
559       return Some(&mStencilAttachment);
560 
561     default:
562       return GetColorAttachPoint(attachPoint);
563   }
564 }
565 
DetachTexture(const WebGLTexture * tex)566 void WebGLFramebuffer::DetachTexture(const WebGLTexture* tex) {
567   for (const auto& attach : mAttachments) {
568     if (attach->Texture() == tex) {
569       attach->Clear();
570     }
571   }
572   InvalidateCaches();
573 }
574 
DetachRenderbuffer(const WebGLRenderbuffer * rb)575 void WebGLFramebuffer::DetachRenderbuffer(const WebGLRenderbuffer* rb) {
576   for (const auto& attach : mAttachments) {
577     if (attach->Renderbuffer() == rb) {
578       attach->Clear();
579     }
580   }
581   InvalidateCaches();
582 }
583 
584 ////////////////////////////////////////////////////////////////////////////////
585 // Completeness
586 
HasDuplicateAttachments() const587 bool WebGLFramebuffer::HasDuplicateAttachments() const {
588   std::set<WebGLFBAttachPoint::Ordered> uniqueAttachSet;
589 
590   for (const auto& attach : mColorAttachments) {
591     if (!attach.HasAttachment()) continue;
592 
593     const WebGLFBAttachPoint::Ordered ordered(attach);
594 
595     const bool didInsert = uniqueAttachSet.insert(ordered).second;
596     if (!didInsert) return true;
597   }
598 
599   return false;
600 }
601 
HasDefinedAttachments() const602 bool WebGLFramebuffer::HasDefinedAttachments() const {
603   bool hasAttachments = false;
604   for (const auto& attach : mAttachments) {
605     hasAttachments |= attach->HasAttachment();
606   }
607   return hasAttachments;
608 }
609 
HasIncompleteAttachments(nsCString * const out_info) const610 bool WebGLFramebuffer::HasIncompleteAttachments(
611     nsCString* const out_info) const {
612   bool hasIncomplete = false;
613   for (const auto& cur : mAttachments) {
614     if (!cur->HasAttachment())
615       continue;  // Not defined, so can't count as incomplete.
616 
617     hasIncomplete |= !cur->IsComplete(mContext, out_info);
618   }
619   return hasIncomplete;
620 }
621 
AllImageRectsMatch() const622 bool WebGLFramebuffer::AllImageRectsMatch() const {
623   MOZ_ASSERT(HasDefinedAttachments());
624   DebugOnly<nsCString> fbStatusInfo;
625   MOZ_ASSERT(!HasIncompleteAttachments(&fbStatusInfo));
626 
627   bool needsInit = true;
628   uint32_t width = 0;
629   uint32_t height = 0;
630 
631   bool hasMismatch = false;
632   for (const auto& attach : mAttachments) {
633     const auto& imageInfo = attach->GetImageInfo();
634     if (!imageInfo) continue;
635 
636     const auto& curWidth = imageInfo->mWidth;
637     const auto& curHeight = imageInfo->mHeight;
638 
639     if (needsInit) {
640       needsInit = false;
641       width = curWidth;
642       height = curHeight;
643       continue;
644     }
645 
646     hasMismatch |= (curWidth != width || curHeight != height);
647   }
648   return !hasMismatch;
649 }
650 
AllImageSamplesMatch() const651 bool WebGLFramebuffer::AllImageSamplesMatch() const {
652   MOZ_ASSERT(HasDefinedAttachments());
653   DebugOnly<nsCString> fbStatusInfo;
654   MOZ_ASSERT(!HasIncompleteAttachments(&fbStatusInfo));
655 
656   bool needsInit = true;
657   uint32_t samples = 0;
658 
659   bool hasMismatch = false;
660   for (const auto& attach : mAttachments) {
661     const auto& imageInfo = attach->GetImageInfo();
662     if (!imageInfo) continue;
663 
664     const auto& curSamples = imageInfo->mSamples;
665 
666     if (needsInit) {
667       needsInit = false;
668       samples = curSamples;
669       continue;
670     }
671 
672     hasMismatch |= (curSamples != samples);
673   };
674   return !hasMismatch;
675 }
676 
PrecheckFramebufferStatus(nsCString * const out_info) const677 FBStatus WebGLFramebuffer::PrecheckFramebufferStatus(
678     nsCString* const out_info) const {
679   MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
680              mContext->mBoundReadFramebuffer == this);
681   if (!HasDefinedAttachments())
682     return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;  // No
683                                                                 // attachments
684 
685   if (HasIncompleteAttachments(out_info))
686     return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
687 
688   if (!AllImageRectsMatch())
689     return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS;  // Inconsistent sizes
690 
691   if (!AllImageSamplesMatch())
692     return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE;  // Inconsistent samples
693 
694   if (HasDuplicateAttachments()) return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED;
695 
696   if (mContext->IsWebGL2()) {
697     MOZ_ASSERT(!mDepthStencilAttachment.HasAttachment());
698     if (mDepthAttachment.HasAttachment() &&
699         mStencilAttachment.HasAttachment()) {
700       if (!mDepthAttachment.IsEquivalentForFeedback(mStencilAttachment))
701         return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED;
702     }
703   } else {
704     const auto depthOrStencilCount =
705         int(mDepthAttachment.HasAttachment()) +
706         int(mStencilAttachment.HasAttachment()) +
707         int(mDepthStencilAttachment.HasAttachment());
708     if (depthOrStencilCount > 1) return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED;
709   }
710 
711   {
712     const WebGLFBAttachPoint* example = nullptr;
713     for (const auto& x : mAttachments) {
714       if (!x->HasAttachment()) continue;
715       if (!example) {
716         example = x;
717         continue;
718       }
719       if (x->ZLayerCount() != example->ZLayerCount()) {
720         return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR;
721       }
722     }
723   }
724 
725   return LOCAL_GL_FRAMEBUFFER_COMPLETE;
726 }
727 
728 ////////////////////////////////////////
729 // Validation
730 
ValidateAndInitAttachments(const GLenum incompleteFbError) const731 bool WebGLFramebuffer::ValidateAndInitAttachments(
732     const GLenum incompleteFbError) const {
733   MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
734              mContext->mBoundReadFramebuffer == this);
735 
736   const auto fbStatus = CheckFramebufferStatus();
737   if (fbStatus == LOCAL_GL_FRAMEBUFFER_COMPLETE) return true;
738 
739   mContext->GenerateError(incompleteFbError, "Framebuffer must be complete.");
740   return false;
741 }
742 
ValidateClearBufferType(GLenum buffer,uint32_t drawBuffer,const webgl::AttribBaseType funcType) const743 bool WebGLFramebuffer::ValidateClearBufferType(
744     GLenum buffer, uint32_t drawBuffer,
745     const webgl::AttribBaseType funcType) const {
746   if (buffer != LOCAL_GL_COLOR) return true;
747 
748   const auto& attach = mColorAttachments[drawBuffer];
749   const auto& imageInfo = attach.GetImageInfo();
750   if (!imageInfo) return true;
751 
752   if (!count(mColorDrawBuffers.begin(), mColorDrawBuffers.end(), &attach))
753     return true;  // DRAW_BUFFERi set to NONE.
754 
755   auto attachType = webgl::AttribBaseType::Float;
756   switch (imageInfo->mFormat->format->componentType) {
757     case webgl::ComponentType::Int:
758       attachType = webgl::AttribBaseType::Int;
759       break;
760     case webgl::ComponentType::UInt:
761       attachType = webgl::AttribBaseType::Uint;
762       break;
763     default:
764       break;
765   }
766 
767   if (attachType != funcType) {
768     mContext->ErrorInvalidOperation(
769         "This attachment is of type %s, but"
770         " this function is of type %s.",
771         ToString(attachType), ToString(funcType));
772     return false;
773   }
774 
775   return true;
776 }
777 
ValidateForColorRead(const webgl::FormatUsageInfo ** const out_format,uint32_t * const out_width,uint32_t * const out_height) const778 bool WebGLFramebuffer::ValidateForColorRead(
779     const webgl::FormatUsageInfo** const out_format, uint32_t* const out_width,
780     uint32_t* const out_height) const {
781   if (!mColorReadBuffer) {
782     mContext->ErrorInvalidOperation("READ_BUFFER must not be NONE.");
783     return false;
784   }
785 
786   if (mColorReadBuffer->ZLayerCount() > 1) {
787     mContext->GenerateError(LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION,
788                             "The READ_BUFFER attachment has multiple views.");
789     return false;
790   }
791 
792   const auto& imageInfo = mColorReadBuffer->GetImageInfo();
793   if (!imageInfo) {
794     mContext->ErrorInvalidOperation(
795         "The READ_BUFFER attachment is not defined.");
796     return false;
797   }
798 
799   if (imageInfo->mSamples) {
800     mContext->ErrorInvalidOperation(
801         "The READ_BUFFER attachment is multisampled.");
802     return false;
803   }
804 
805   *out_format = imageInfo->mFormat;
806   *out_width = imageInfo->mWidth;
807   *out_height = imageInfo->mHeight;
808   return true;
809 }
810 
811 ////////////////////////////////////////////////////////////////////////////////
812 // Resolution and caching
813 
DoDeferredAttachments() const814 void WebGLFramebuffer::DoDeferredAttachments() const {
815   if (mContext->IsWebGL2()) return;
816 
817   const auto& gl = mContext->gl;
818   gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT,
819                                LOCAL_GL_RENDERBUFFER, 0);
820   gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
821                                LOCAL_GL_STENCIL_ATTACHMENT,
822                                LOCAL_GL_RENDERBUFFER, 0);
823 
824   const auto fn = [&](const WebGLFBAttachPoint& attach) {
825     MOZ_ASSERT(attach.mDeferAttachment);
826     if (attach.HasAttachment()) {
827       attach.DoAttachment(gl);
828     }
829   };
830   // Only one of these will have an attachment.
831   fn(mDepthAttachment);
832   fn(mStencilAttachment);
833   fn(mDepthStencilAttachment);
834 }
835 
ResolveAttachmentData() const836 void WebGLFramebuffer::ResolveAttachmentData() const {
837   // GLES 3.0.5 p188:
838   //   The result of clearing integer color buffers with `Clear` is undefined.
839 
840   // Two different approaches:
841   // On WebGL 2, we have glClearBuffer, and *must* use it for integer buffers,
842   // so let's just use it for all the buffers. One WebGL 1, we might not have
843   // glClearBuffer,
844 
845   // WebGL 1 is easier, because we can just call glClear, possibly with
846   // glDrawBuffers.
847 
848   const auto& gl = mContext->gl;
849 
850   const webgl::ScopedPrepForResourceClear scopedPrep(*mContext);
851 
852   if (mContext->IsWebGL2()) {
853     const uint32_t uiZeros[4] = {};
854     const int32_t iZeros[4] = {};
855     const float fZeros[4] = {};
856     const float fOne[] = {1.0f};
857 
858     for (const auto& cur : mAttachments) {
859       const auto& imageInfo = cur->GetImageInfo();
860       if (!imageInfo || !imageInfo->mUninitializedSlices)
861         continue;  // Nothing attached, or already has data.
862 
863       const auto fnClearBuffer = [&]() {
864         const auto& format = imageInfo->mFormat->format;
865         MOZ_ASSERT(format->estimatedBytesPerPixel <= sizeof(uiZeros));
866         MOZ_ASSERT(format->estimatedBytesPerPixel <= sizeof(iZeros));
867         MOZ_ASSERT(format->estimatedBytesPerPixel <= sizeof(fZeros));
868 
869         switch (cur->mAttachmentPoint) {
870           case LOCAL_GL_DEPTH_ATTACHMENT:
871             gl->fClearBufferfv(LOCAL_GL_DEPTH, 0, fOne);
872             break;
873           case LOCAL_GL_STENCIL_ATTACHMENT:
874             gl->fClearBufferiv(LOCAL_GL_STENCIL, 0, iZeros);
875             break;
876           default:
877             MOZ_ASSERT(cur->mAttachmentPoint !=
878                        LOCAL_GL_DEPTH_STENCIL_ATTACHMENT);
879             const uint32_t drawBuffer =
880                 cur->mAttachmentPoint - LOCAL_GL_COLOR_ATTACHMENT0;
881             MOZ_ASSERT(drawBuffer <= 100);
882             switch (format->componentType) {
883               case webgl::ComponentType::Int:
884                 gl->fClearBufferiv(LOCAL_GL_COLOR, drawBuffer, iZeros);
885                 break;
886               case webgl::ComponentType::UInt:
887                 gl->fClearBufferuiv(LOCAL_GL_COLOR, drawBuffer, uiZeros);
888                 break;
889               default:
890                 gl->fClearBufferfv(LOCAL_GL_COLOR, drawBuffer, fZeros);
891                 break;
892             }
893         }
894       };
895 
896       if (imageInfo->mDepth > 1) {
897         const auto& tex = cur->Texture();
898         const gl::ScopedFramebuffer scopedFB(gl);
899         const gl::ScopedBindFramebuffer scopedBindFB(gl, scopedFB.FB());
900         for (const auto z : IntegerRange(imageInfo->mDepth)) {
901           if ((*imageInfo->mUninitializedSlices)[z]) {
902             gl->fFramebufferTextureLayer(LOCAL_GL_FRAMEBUFFER,
903                                          cur->mAttachmentPoint, tex->mGLName,
904                                          cur->MipLevel(), z);
905             fnClearBuffer();
906           }
907         }
908       } else {
909         fnClearBuffer();
910       }
911       imageInfo->mUninitializedSlices = Nothing();
912     }
913     return;
914   }
915 
916   uint32_t clearBits = 0;
917   std::vector<GLenum> drawBufferForClear;
918 
919   const auto fnGather = [&](const WebGLFBAttachPoint& attach,
920                             const uint32_t attachClearBits) {
921     const auto& imageInfo = attach.GetImageInfo();
922     if (!imageInfo || !imageInfo->mUninitializedSlices) return false;
923 
924     clearBits |= attachClearBits;
925     imageInfo->mUninitializedSlices = Nothing();  // Just mark it now.
926     return true;
927   };
928 
929   //////
930 
931   for (const auto& cur : mColorAttachments) {
932     if (fnGather(cur, LOCAL_GL_COLOR_BUFFER_BIT)) {
933       const uint32_t id = cur.mAttachmentPoint - LOCAL_GL_COLOR_ATTACHMENT0;
934       MOZ_ASSERT(id <= 100);
935       drawBufferForClear.resize(id + 1);  // Pads with zeros!
936       drawBufferForClear[id] = cur.mAttachmentPoint;
937     }
938   }
939 
940   (void)fnGather(mDepthAttachment, LOCAL_GL_DEPTH_BUFFER_BIT);
941   (void)fnGather(mStencilAttachment, LOCAL_GL_STENCIL_BUFFER_BIT);
942   (void)fnGather(mDepthStencilAttachment,
943                  LOCAL_GL_DEPTH_BUFFER_BIT | LOCAL_GL_STENCIL_BUFFER_BIT);
944 
945   //////
946 
947   if (!clearBits) return;
948 
949   if (gl->IsSupported(gl::GLFeature::draw_buffers)) {
950     gl->fDrawBuffers(drawBufferForClear.size(), drawBufferForClear.data());
951   }
952 
953   gl->fClear(clearBits);
954 
955   RefreshDrawBuffers();
956 }
957 
~CompletenessInfo()958 WebGLFramebuffer::CompletenessInfo::~CompletenessInfo() {
959   if (!this->fb) return;
960   const auto& fb = *this->fb;
961   const auto& webgl = fb.mContext;
962   fb.mNumFBStatusInvals++;
963   if (fb.mNumFBStatusInvals > webgl->mMaxAcceptableFBStatusInvals) {
964     webgl->GeneratePerfWarning(
965         "FB was invalidated after being complete %u"
966         " times. [webgl.perf.max-acceptable-fb-status-invals]",
967         uint32_t(fb.mNumFBStatusInvals));
968   }
969 }
970 
971 ////////////////////////////////////////////////////////////////////////////////
972 // Entrypoints
973 
CheckFramebufferStatus() const974 FBStatus WebGLFramebuffer::CheckFramebufferStatus() const {
975   if (MOZ_UNLIKELY(mOpaque && !mInOpaqueRAF)) {
976     // Opaque Framebuffers are considered incomplete outside of a RAF.
977     return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED;
978   }
979 
980   if (mCompletenessInfo) return LOCAL_GL_FRAMEBUFFER_COMPLETE;
981 
982   // Ok, let's try to resolve it!
983 
984   nsCString statusInfo;
985   FBStatus ret = PrecheckFramebufferStatus(&statusInfo);
986   do {
987     if (ret != LOCAL_GL_FRAMEBUFFER_COMPLETE) break;
988 
989     // Looks good on our end. Let's ask the driver.
990     gl::GLContext* const gl = mContext->gl;
991 
992     const ScopedFBRebinder autoFB(mContext);
993     gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mGLName);
994 
995     ////
996 
997     DoDeferredAttachments();
998     RefreshDrawBuffers();
999     RefreshReadBuffer();
1000 
1001     ret = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
1002 
1003     ////
1004 
1005     if (ret != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
1006       const nsPrintfCString text("Bad status according to the driver: 0x%04x",
1007                                  ret.get());
1008       statusInfo = text;
1009       break;
1010     }
1011 
1012     ResolveAttachmentData();
1013 
1014     // Sweet, let's cache that.
1015     auto info = CompletenessInfo{this};
1016     mCompletenessInfo.ResetInvalidators({});
1017     mCompletenessInfo.AddInvalidator(*this);
1018 
1019     const auto fnIsFloat32 = [](const webgl::FormatInfo& info) {
1020       if (info.componentType != webgl::ComponentType::Float) return false;
1021       return info.r == 32;
1022     };
1023 
1024     for (const auto& cur : mAttachments) {
1025       const auto& tex = cur->Texture();
1026       const auto& rb = cur->Renderbuffer();
1027       if (tex) {
1028         mCompletenessInfo.AddInvalidator(*tex);
1029         info.texAttachments.push_back(cur);
1030       } else if (rb) {
1031         mCompletenessInfo.AddInvalidator(*rb);
1032       } else {
1033         continue;
1034       }
1035       const auto& imageInfo = cur->GetImageInfo();
1036       MOZ_ASSERT(imageInfo);
1037 
1038       const auto maybeColorId = cur->ColorAttachmentId();
1039       if (maybeColorId) {
1040         const auto id = *maybeColorId;
1041         info.hasAttachment[id] = true;
1042         info.isAttachmentF32[id] = fnIsFloat32(*imageInfo->mFormat->format);
1043       }
1044 
1045       info.width = imageInfo->mWidth;
1046       info.height = imageInfo->mHeight;
1047       info.zLayerCount = cur->ZLayerCount();
1048       info.isMultiview = cur->IsMultiview();
1049     }
1050     MOZ_ASSERT(info.width && info.height);
1051     mCompletenessInfo = Some(std::move(info));
1052     info.fb = nullptr;  // Don't trigger the invalidation warning.
1053     return LOCAL_GL_FRAMEBUFFER_COMPLETE;
1054   } while (false);
1055 
1056   MOZ_ASSERT(ret != LOCAL_GL_FRAMEBUFFER_COMPLETE);
1057   mContext->GenerateWarning("Framebuffer not complete. (status: 0x%04x) %s",
1058                             ret.get(), statusInfo.BeginReading());
1059   return ret;
1060 }
1061 
1062 ////
1063 
RefreshDrawBuffers() const1064 void WebGLFramebuffer::RefreshDrawBuffers() const {
1065   const auto& gl = mContext->gl;
1066   if (!gl->IsSupported(gl::GLFeature::draw_buffers)) return;
1067 
1068   // Prior to GL4.1, having a no-image FB attachment that's selected by
1069   // DrawBuffers yields a framebuffer status of
1070   // FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER. We could workaround this only on
1071   // affected versions, but it's easier be unconditional.
1072   std::vector<GLenum> driverBuffers(mContext->Limits().maxColorDrawBuffers,
1073                                     LOCAL_GL_NONE);
1074   for (const auto& attach : mColorDrawBuffers) {
1075     if (attach->HasAttachment()) {
1076       const uint32_t index =
1077           attach->mAttachmentPoint - LOCAL_GL_COLOR_ATTACHMENT0;
1078       driverBuffers[index] = attach->mAttachmentPoint;
1079     }
1080   }
1081 
1082   gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, mGLName);
1083   gl->fDrawBuffers(driverBuffers.size(), driverBuffers.data());
1084 }
1085 
RefreshReadBuffer() const1086 void WebGLFramebuffer::RefreshReadBuffer() const {
1087   const auto& gl = mContext->gl;
1088   if (!gl->IsSupported(gl::GLFeature::read_buffer)) return;
1089 
1090   // Prior to GL4.1, having a no-image FB attachment that's selected by
1091   // ReadBuffer yields a framebuffer status of
1092   // FRAMEBUFFER_INCOMPLETE_READ_BUFFER. We could workaround this only on
1093   // affected versions, but it's easier be unconditional.
1094   GLenum driverBuffer = LOCAL_GL_NONE;
1095   if (mColorReadBuffer && mColorReadBuffer->HasAttachment()) {
1096     driverBuffer = mColorReadBuffer->mAttachmentPoint;
1097   }
1098 
1099   gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, mGLName);
1100   gl->fReadBuffer(driverBuffer);
1101 }
1102 
1103 ////
1104 
DrawBuffers(const std::vector<GLenum> & buffers)1105 void WebGLFramebuffer::DrawBuffers(const std::vector<GLenum>& buffers) {
1106   if (buffers.size() > mContext->MaxValidDrawBuffers()) {
1107     // "An INVALID_VALUE error is generated if `n` is greater than
1108     // MAX_DRAW_BUFFERS."
1109     mContext->ErrorInvalidValue(
1110         "`buffers` must have a length <="
1111         " MAX_DRAW_BUFFERS.");
1112     return;
1113   }
1114 
1115   std::vector<const WebGLFBAttachPoint*> newColorDrawBuffers;
1116   newColorDrawBuffers.reserve(buffers.size());
1117 
1118   mDrawBufferEnabled.reset();
1119   for (const auto i : IntegerRange(buffers.size())) {
1120     // "If the GL is bound to a draw framebuffer object, the `i`th buffer listed
1121     // in bufs must be COLOR_ATTACHMENTi or NONE. Specifying a buffer out of
1122     // order, BACK, or COLOR_ATTACHMENTm where `m` is greater than or equal to
1123     // the value of MAX_COLOR_ATTACHMENTS, will generate the error
1124     // INVALID_OPERATION.
1125 
1126     // WEBGL_draw_buffers:
1127     // "The value of the MAX_COLOR_ATTACHMENTS_WEBGL parameter must be greater
1128     // than or equal to that of the MAX_DRAW_BUFFERS_WEBGL parameter." This
1129     // means that if buffers.Length() isn't larger than MaxDrawBuffers, it won't
1130     // be larger than MaxColorAttachments.
1131     const auto& cur = buffers[i];
1132     if (cur == LOCAL_GL_COLOR_ATTACHMENT0 + i) {
1133       const auto& attach = mColorAttachments[i];
1134       newColorDrawBuffers.push_back(&attach);
1135       mDrawBufferEnabled[i] = true;
1136     } else if (cur != LOCAL_GL_NONE) {
1137       const bool isColorEnum = (cur >= LOCAL_GL_COLOR_ATTACHMENT0 &&
1138                                 cur < mContext->LastColorAttachmentEnum());
1139       if (cur != LOCAL_GL_BACK && !isColorEnum) {
1140         mContext->ErrorInvalidEnum("Unexpected enum in buffers.");
1141         return;
1142       }
1143 
1144       mContext->ErrorInvalidOperation(
1145           "`buffers[i]` must be NONE or"
1146           " COLOR_ATTACHMENTi.");
1147       return;
1148     }
1149   }
1150 
1151   ////
1152 
1153   mColorDrawBuffers = std::move(newColorDrawBuffers);
1154   RefreshDrawBuffers();  // Calls glDrawBuffers.
1155 }
1156 
ReadBuffer(GLenum attachPoint)1157 void WebGLFramebuffer::ReadBuffer(GLenum attachPoint) {
1158   const auto& maybeAttach = GetColorAttachPoint(attachPoint);
1159   if (!maybeAttach) {
1160     const char text[] =
1161         "`mode` must be a COLOR_ATTACHMENTi, for 0 <= i <"
1162         " MAX_DRAW_BUFFERS.";
1163     if (attachPoint == LOCAL_GL_BACK) {
1164       mContext->ErrorInvalidOperation(text);
1165     } else {
1166       mContext->ErrorInvalidEnum(text);
1167     }
1168     return;
1169   }
1170   const auto& attach = maybeAttach.value();  // Might be nullptr.
1171 
1172   ////
1173 
1174   mColorReadBuffer = attach;
1175   RefreshReadBuffer();  // Calls glReadBuffer.
1176 }
1177 
1178 ////
1179 
FramebufferAttach(const GLenum attachEnum,const webgl::FbAttachInfo & toAttach)1180 bool WebGLFramebuffer::FramebufferAttach(const GLenum attachEnum,
1181                                          const webgl::FbAttachInfo& toAttach) {
1182   MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
1183              mContext->mBoundReadFramebuffer == this);
1184 
1185   if (MOZ_UNLIKELY(mOpaque)) {
1186     // An opaque framebuffer's attachments cannot be inspected or changed.
1187     return false;
1188   }
1189 
1190   // `attachment`
1191   const auto maybeAttach = GetAttachPoint(attachEnum);
1192   if (!maybeAttach || !maybeAttach.value()) return false;
1193   const auto& attach = maybeAttach.value();
1194 
1195   const auto& gl = mContext->gl;
1196   gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mGLName);
1197   if (mContext->IsWebGL2() && attachEnum == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
1198     mDepthAttachment.Set(gl, toAttach);
1199     mStencilAttachment.Set(gl, toAttach);
1200   } else {
1201     attach->Set(gl, toAttach);
1202   }
1203   InvalidateCaches();
1204   return true;
1205 }
1206 
GetAttachmentParameter(GLenum attachEnum,GLenum pname)1207 Maybe<double> WebGLFramebuffer::GetAttachmentParameter(GLenum attachEnum,
1208                                                        GLenum pname) {
1209   const auto maybeAttach = GetAttachPoint(attachEnum);
1210   if (!maybeAttach || attachEnum == LOCAL_GL_NONE) {
1211     mContext->ErrorInvalidEnum(
1212         "Can only query COLOR_ATTACHMENTi,"
1213         " DEPTH_ATTACHMENT, DEPTH_STENCIL_ATTACHMENT, or"
1214         " STENCIL_ATTACHMENT for a framebuffer.");
1215     return Nothing();
1216   }
1217   if (MOZ_UNLIKELY(mOpaque)) {
1218     mContext->ErrorInvalidOperation(
1219         "An opaque framebuffer's attachments cannot be inspected or changed.");
1220     return Nothing();
1221   }
1222   auto attach = maybeAttach.value();
1223 
1224   if (mContext->IsWebGL2() && attachEnum == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
1225     // There are a couple special rules for this one.
1226 
1227     if (pname == LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE) {
1228       mContext->ErrorInvalidOperation(
1229           "Querying"
1230           " FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE"
1231           " against DEPTH_STENCIL_ATTACHMENT is an"
1232           " error.");
1233       return Nothing();
1234     }
1235 
1236     if (mDepthAttachment.Renderbuffer() != mStencilAttachment.Renderbuffer() ||
1237         mDepthAttachment.Texture() != mStencilAttachment.Texture()) {
1238       mContext->ErrorInvalidOperation(
1239           "DEPTH_ATTACHMENT and STENCIL_ATTACHMENT"
1240           " have different objects bound.");
1241       return Nothing();
1242     }
1243 
1244     attach = &mDepthAttachment;
1245   }
1246 
1247   return attach->GetParameter(mContext, attachEnum, pname);
1248 }
1249 
1250 ////////////////////
1251 
GetBackbufferFormats(const WebGLContext * webgl,const webgl::FormatInfo ** const out_color,const webgl::FormatInfo ** const out_depth,const webgl::FormatInfo ** const out_stencil)1252 static void GetBackbufferFormats(const WebGLContext* webgl,
1253                                  const webgl::FormatInfo** const out_color,
1254                                  const webgl::FormatInfo** const out_depth,
1255                                  const webgl::FormatInfo** const out_stencil) {
1256   const auto& options = webgl->Options();
1257 
1258   const auto effFormat = (options.alpha ? webgl::EffectiveFormat::RGBA8
1259                                         : webgl::EffectiveFormat::RGB8);
1260   *out_color = webgl::GetFormat(effFormat);
1261 
1262   *out_depth = nullptr;
1263   *out_stencil = nullptr;
1264   if (options.depth && options.stencil) {
1265     *out_depth = webgl::GetFormat(webgl::EffectiveFormat::DEPTH24_STENCIL8);
1266     *out_stencil = *out_depth;
1267   } else {
1268     if (options.depth) {
1269       *out_depth = webgl::GetFormat(webgl::EffectiveFormat::DEPTH_COMPONENT16);
1270     }
1271     if (options.stencil) {
1272       *out_stencil = webgl::GetFormat(webgl::EffectiveFormat::STENCIL_INDEX8);
1273     }
1274   }
1275 }
1276 
1277 /*static*/
BlitFramebuffer(WebGLContext * webgl,GLint _srcX0,GLint _srcY0,GLint _srcX1,GLint _srcY1,GLint _dstX0,GLint _dstY0,GLint _dstX1,GLint _dstY1,GLbitfield mask,GLenum filter)1278 void WebGLFramebuffer::BlitFramebuffer(WebGLContext* webgl, GLint _srcX0,
1279                                        GLint _srcY0, GLint _srcX1, GLint _srcY1,
1280                                        GLint _dstX0, GLint _dstY0, GLint _dstX1,
1281                                        GLint _dstY1, GLbitfield mask,
1282                                        GLenum filter) {
1283   auto srcP0 = ivec2{_srcX0, _srcY0};
1284   auto srcP1 = ivec2{_srcX1, _srcY1};
1285   auto dstP0 = ivec2{_dstX0, _dstY0};
1286   auto dstP1 = ivec2{_dstX1, _dstY1};
1287 
1288   const GLbitfield depthAndStencilBits =
1289       LOCAL_GL_DEPTH_BUFFER_BIT | LOCAL_GL_STENCIL_BUFFER_BIT;
1290   if (bool(mask & depthAndStencilBits) && filter == LOCAL_GL_LINEAR) {
1291     webgl->ErrorInvalidOperation(
1292         "DEPTH_BUFFER_BIT and STENCIL_BUFFER_BIT can"
1293         " only be used with NEAREST filtering.");
1294     return;
1295   }
1296 
1297   const auto& srcFB = webgl->mBoundReadFramebuffer;
1298   const auto& dstFB = webgl->mBoundDrawFramebuffer;
1299 
1300   ////
1301   // Collect data
1302 
1303   const auto fnGetFormat =
1304       [](const WebGLFBAttachPoint& cur,
1305          bool* const out_hasSamples) -> const webgl::FormatInfo* {
1306     const auto& imageInfo = cur.GetImageInfo();
1307     if (!imageInfo) return nullptr;  // No attachment.
1308     *out_hasSamples = bool(imageInfo->mSamples);
1309     return imageInfo->mFormat->format;
1310   };
1311 
1312   bool srcHasSamples = false;
1313   bool srcIsFilterable = true;
1314   const webgl::FormatInfo* srcColorFormat;
1315   const webgl::FormatInfo* srcDepthFormat;
1316   const webgl::FormatInfo* srcStencilFormat;
1317   gfx::IntSize srcSize;
1318 
1319   if (srcFB) {
1320     const auto& info = *srcFB->GetCompletenessInfo();
1321     if (info.zLayerCount != 1) {
1322       webgl->GenerateError(
1323           LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION,
1324           "Source framebuffer cannot have more than one multiview layer.");
1325       return;
1326     }
1327     srcColorFormat = nullptr;
1328     if (srcFB->mColorReadBuffer) {
1329       const auto& imageInfo = srcFB->mColorReadBuffer->GetImageInfo();
1330       if (imageInfo) {
1331         srcIsFilterable &= imageInfo->mFormat->isFilterable;
1332       }
1333       srcColorFormat = fnGetFormat(*(srcFB->mColorReadBuffer), &srcHasSamples);
1334     }
1335     srcDepthFormat = fnGetFormat(srcFB->DepthAttachment(), &srcHasSamples);
1336     srcStencilFormat = fnGetFormat(srcFB->StencilAttachment(), &srcHasSamples);
1337     MOZ_ASSERT(!srcFB->DepthStencilAttachment().HasAttachment());
1338     srcSize = {info.width, info.height};
1339   } else {
1340     srcHasSamples = false;  // Always false.
1341 
1342     GetBackbufferFormats(webgl, &srcColorFormat, &srcDepthFormat,
1343                          &srcStencilFormat);
1344     const auto& size = webgl->DrawingBufferSize();
1345     srcSize = {size.x, size.y};
1346   }
1347 
1348   ////
1349 
1350   bool dstHasSamples = false;
1351   const webgl::FormatInfo* dstDepthFormat;
1352   const webgl::FormatInfo* dstStencilFormat;
1353   bool dstHasColor = false;
1354   bool colorFormatsMatch = true;
1355   bool colorTypesMatch = true;
1356   bool colorSrgbMatches = true;
1357   gfx::IntSize dstSize;
1358 
1359   const auto fnCheckColorFormat = [&](const webgl::FormatInfo* dstFormat) {
1360     MOZ_ASSERT(dstFormat->r || dstFormat->g || dstFormat->b || dstFormat->a);
1361     dstHasColor = true;
1362     colorFormatsMatch &= (dstFormat == srcColorFormat);
1363     colorTypesMatch &=
1364         srcColorFormat && (dstFormat->baseType == srcColorFormat->baseType);
1365     colorSrgbMatches &=
1366         srcColorFormat && (dstFormat->isSRGB == srcColorFormat->isSRGB);
1367   };
1368 
1369   if (dstFB) {
1370     for (const auto& cur : dstFB->mColorDrawBuffers) {
1371       const auto& format = fnGetFormat(*cur, &dstHasSamples);
1372       if (!format) continue;
1373 
1374       fnCheckColorFormat(format);
1375     }
1376 
1377     dstDepthFormat = fnGetFormat(dstFB->DepthAttachment(), &dstHasSamples);
1378     dstStencilFormat = fnGetFormat(dstFB->StencilAttachment(), &dstHasSamples);
1379     MOZ_ASSERT(!dstFB->DepthStencilAttachment().HasAttachment());
1380 
1381     const auto& info = *dstFB->GetCompletenessInfo();
1382     if (info.isMultiview) {
1383       webgl->GenerateError(
1384           LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION,
1385           "Destination framebuffer cannot have multiview attachments.");
1386       return;
1387     }
1388     dstSize = {info.width, info.height};
1389   } else {
1390     dstHasSamples = webgl->Options().antialias;
1391 
1392     const webgl::FormatInfo* dstColorFormat;
1393     GetBackbufferFormats(webgl, &dstColorFormat, &dstDepthFormat,
1394                          &dstStencilFormat);
1395 
1396     fnCheckColorFormat(dstColorFormat);
1397 
1398     const auto& size = webgl->DrawingBufferSize();
1399     dstSize = {size.x, size.y};
1400   }
1401 
1402   ////
1403   // Clear unused buffer bits
1404 
1405   if (mask & LOCAL_GL_COLOR_BUFFER_BIT && !srcColorFormat && !dstHasColor) {
1406     mask ^= LOCAL_GL_COLOR_BUFFER_BIT;
1407   }
1408 
1409   if (mask & LOCAL_GL_DEPTH_BUFFER_BIT && !srcDepthFormat && !dstDepthFormat) {
1410     mask ^= LOCAL_GL_DEPTH_BUFFER_BIT;
1411   }
1412 
1413   if (mask & LOCAL_GL_STENCIL_BUFFER_BIT && !srcStencilFormat &&
1414       !dstStencilFormat) {
1415     mask ^= LOCAL_GL_STENCIL_BUFFER_BIT;
1416   }
1417 
1418   ////
1419   // Validation
1420 
1421   if (dstHasSamples) {
1422     webgl->ErrorInvalidOperation(
1423         "DRAW_FRAMEBUFFER may not have multiple"
1424         " samples.");
1425     return;
1426   }
1427 
1428   bool requireFilterable = (filter == LOCAL_GL_LINEAR);
1429   if (srcHasSamples) {
1430     requireFilterable = false;  // It picks one.
1431 
1432     if (mask & LOCAL_GL_COLOR_BUFFER_BIT && dstHasColor && !colorFormatsMatch) {
1433       webgl->ErrorInvalidOperation(
1434           "Color buffer formats must match if"
1435           " selected, when reading from a multisampled"
1436           " source.");
1437       return;
1438     }
1439 
1440     if (srcP0 != dstP0 || srcP1 != dstP1) {
1441       webgl->ErrorInvalidOperation(
1442           "If the source is multisampled, then the"
1443           " source and dest regions must match exactly.");
1444       return;
1445     }
1446   }
1447 
1448   // -
1449 
1450   if (mask & LOCAL_GL_COLOR_BUFFER_BIT) {
1451     if (requireFilterable && !srcIsFilterable) {
1452       webgl->ErrorInvalidOperation(
1453           "`filter` is LINEAR and READ_BUFFER"
1454           " contains integer data.");
1455       return;
1456     }
1457 
1458     if (!colorTypesMatch) {
1459       webgl->ErrorInvalidOperation(
1460           "Color component types (float/uint/"
1461           "int) must match.");
1462       return;
1463     }
1464   }
1465 
1466   /* GLES 3.0.4, p199:
1467    *   Calling BlitFramebuffer will result in an INVALID_OPERATION error if
1468    *   mask includes DEPTH_BUFFER_BIT or STENCIL_BUFFER_BIT, and the source
1469    *   and destination depth and stencil buffer formats do not match.
1470    *
1471    * jgilbert: The wording is such that if only DEPTH_BUFFER_BIT is specified,
1472    * the stencil formats must match. This seems wrong. It could be a spec bug,
1473    * or I could be missing an interaction in one of the earlier paragraphs.
1474    */
1475   if (mask & LOCAL_GL_DEPTH_BUFFER_BIT && dstDepthFormat &&
1476       dstDepthFormat != srcDepthFormat) {
1477     webgl->ErrorInvalidOperation(
1478         "Depth buffer formats must match if selected.");
1479     return;
1480   }
1481 
1482   if (mask & LOCAL_GL_STENCIL_BUFFER_BIT && dstStencilFormat &&
1483       dstStencilFormat != srcStencilFormat) {
1484     webgl->ErrorInvalidOperation(
1485         "Stencil buffer formats must match if selected.");
1486     return;
1487   }
1488 
1489   ////
1490   // Check for feedback
1491 
1492   if (srcFB && dstFB) {
1493     const WebGLFBAttachPoint* feedback = nullptr;
1494 
1495     if (mask & LOCAL_GL_COLOR_BUFFER_BIT) {
1496       MOZ_ASSERT(srcFB->mColorReadBuffer->HasAttachment());
1497       for (const auto& cur : dstFB->mColorDrawBuffers) {
1498         if (srcFB->mColorReadBuffer->IsEquivalentForFeedback(*cur)) {
1499           feedback = cur;
1500           break;
1501         }
1502       }
1503     }
1504 
1505     if (mask & LOCAL_GL_DEPTH_BUFFER_BIT &&
1506         srcFB->DepthAttachment().IsEquivalentForFeedback(
1507             dstFB->DepthAttachment())) {
1508       feedback = &dstFB->DepthAttachment();
1509     }
1510 
1511     if (mask & LOCAL_GL_STENCIL_BUFFER_BIT &&
1512         srcFB->StencilAttachment().IsEquivalentForFeedback(
1513             dstFB->StencilAttachment())) {
1514       feedback = &dstFB->StencilAttachment();
1515     }
1516 
1517     if (feedback) {
1518       webgl->ErrorInvalidOperation(
1519           "Feedback detected into DRAW_FRAMEBUFFER's"
1520           " 0x%04x attachment.",
1521           feedback->mAttachmentPoint);
1522       return;
1523     }
1524   } else if (!srcFB && !dstFB) {
1525     webgl->ErrorInvalidOperation("Feedback with default framebuffer.");
1526     return;
1527   }
1528 
1529   // -
1530   // Mutually constrain src and dst rects for eldritch blits.
1531 
1532   [&] {
1533     using fvec2 = avec2<float>;  // Switch to float, because there's no perfect
1534                                  // solution anyway.
1535 
1536     const auto zero2f = fvec2{0, 0};
1537     const auto srcSizef = AsVec(srcSize).StaticCast<fvec2>();
1538     const auto dstSizef = AsVec(dstSize).StaticCast<fvec2>();
1539 
1540     const auto srcP0f = srcP0.StaticCast<fvec2>();
1541     const auto srcP1f = srcP1.StaticCast<fvec2>();
1542     const auto dstP0f = dstP0.StaticCast<fvec2>();
1543     const auto dstP1f = dstP1.StaticCast<fvec2>();
1544 
1545     const auto srcRectDiff = srcP1f - srcP0f;
1546     const auto dstRectDiff = dstP1f - dstP0f;
1547 
1548     // Skip if zero-sized.
1549     if (!srcRectDiff.x || !srcRectDiff.y || !dstRectDiff.x || !dstRectDiff.y) {
1550       srcP0 = srcP1 = dstP0 = dstP1 = {0, 0};
1551       return;
1552     }
1553 
1554     // Clamp the rect points
1555     const auto srcQ0 = srcP0f.ClampMinMax(zero2f, srcSizef);
1556     const auto srcQ1 = srcP1f.ClampMinMax(zero2f, srcSizef);
1557 
1558     // Normalized to the [0,1] abstact copy rect
1559     const auto srcQ0Norm = (srcQ0 - srcP0f) / srcRectDiff;
1560     const auto srcQ1Norm = (srcQ1 - srcP0f) / srcRectDiff;
1561 
1562     // Map into dst
1563     const auto srcQ0InDst = dstP0f + srcQ0Norm * dstRectDiff;
1564     const auto srcQ1InDst = dstP0f + srcQ1Norm * dstRectDiff;
1565 
1566     // Clamp the rect points
1567     const auto dstQ0 = srcQ0InDst.ClampMinMax(zero2f, dstSizef);
1568     const auto dstQ1 = srcQ1InDst.ClampMinMax(zero2f, dstSizef);
1569 
1570     // Alright, time to go back to src!
1571     // Normalized to the [0,1] abstact copy rect
1572     const auto dstQ0Norm = (dstQ0 - dstP0f) / dstRectDiff;
1573     const auto dstQ1Norm = (dstQ1 - dstP0f) / dstRectDiff;
1574 
1575     // Map into src
1576     const auto dstQ0InSrc = srcP0f + dstQ0Norm * srcRectDiff;
1577     const auto dstQ1InSrc = srcP0f + dstQ1Norm * srcRectDiff;
1578 
1579     const auto srcQ0Constrained = dstQ0InSrc.ClampMinMax(zero2f, srcSizef);
1580     const auto srcQ1Constrained = dstQ1InSrc.ClampMinMax(zero2f, srcSizef);
1581 
1582     // Round, don't floor:
1583     srcP0 = (srcQ0Constrained + 0.5).StaticCast<ivec2>();
1584     srcP1 = (srcQ1Constrained + 0.5).StaticCast<ivec2>();
1585     dstP0 = (dstQ0 + 0.5).StaticCast<ivec2>();
1586     dstP1 = (dstQ1 + 0.5).StaticCast<ivec2>();
1587   }();
1588 
1589   bool inBounds = true;
1590   inBounds &= (srcP0 == srcP0.Clamp({0, 0}, AsVec(srcSize)));
1591   inBounds &= (srcP1 == srcP1.Clamp({0, 0}, AsVec(srcSize)));
1592   inBounds &= (dstP0 == dstP0.Clamp({0, 0}, AsVec(dstSize)));
1593   inBounds &= (dstP1 == dstP1.Clamp({0, 0}, AsVec(dstSize)));
1594   if (!inBounds) {
1595     webgl->ErrorImplementationBug(
1596         "Subrects still not within src and dst after constraining.");
1597     return;
1598   }
1599 
1600   // -
1601   // Execute as constrained
1602 
1603   const auto& gl = webgl->gl;
1604   const ScopedDrawCallWrapper wrapper(*webgl);
1605 
1606   gl->fBlitFramebuffer(srcP0.x, srcP0.y, srcP1.x, srcP1.y, dstP0.x, dstP0.y,
1607                        dstP1.x, dstP1.y, mask, filter);
1608 
1609   // -
1610 
1611   if (mask & LOCAL_GL_COLOR_BUFFER_BIT && !colorSrgbMatches && !gl->IsGLES() &&
1612       gl->Version() < 440) {
1613     // Mostly for Mac.
1614     // Remember, we have to filter in the *linear* format blit.
1615 
1616     // src -Blit-> fbB -DrawBlit-> fbC -Blit-> dst
1617 
1618     const auto fbB = gl::MozFramebuffer::Create(gl, {1, 1}, 0, false);
1619     const auto fbC = gl::MozFramebuffer::Create(gl, {1, 1}, 0, false);
1620 
1621     // -
1622 
1623     auto sizeBC = srcSize;
1624     GLenum formatC = LOCAL_GL_RGBA8;
1625     if (srcColorFormat->isSRGB) {
1626       // srgb -> linear
1627     } else {
1628       // linear -> srgb
1629       sizeBC = dstSize;
1630       formatC = LOCAL_GL_SRGB8_ALPHA8;
1631     }
1632 
1633     const auto fnSetTex = [&](const gl::MozFramebuffer& fb,
1634                               const GLenum format) {
1635       const gl::ScopedBindTexture bindTex(gl, fb.ColorTex());
1636       gl->fTexStorage2D(LOCAL_GL_TEXTURE_2D, 1, format, sizeBC.width,
1637                         sizeBC.height);
1638     };
1639     fnSetTex(*fbB, srcColorFormat->sizedFormat);
1640     fnSetTex(*fbC, formatC);
1641 
1642     // -
1643 
1644     {
1645       const gl::ScopedBindFramebuffer bindFb(gl);
1646       gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, fbB->mFB);
1647 
1648       if (srcColorFormat->isSRGB) {
1649         // srgb -> linear
1650         gl->fBlitFramebuffer(srcP0.x, srcP0.y, srcP1.x, srcP1.y, srcP0.x,
1651                              srcP0.y, srcP1.x, srcP1.y,
1652                              LOCAL_GL_COLOR_BUFFER_BIT, LOCAL_GL_NEAREST);
1653       } else {
1654         // linear -> srgb
1655         gl->fBlitFramebuffer(srcP0.x, srcP0.y, srcP1.x, srcP1.y, dstP0.x,
1656                              dstP0.y, dstP1.x, dstP1.y,
1657                              LOCAL_GL_COLOR_BUFFER_BIT, filter);
1658       }
1659 
1660       const auto& blitHelper = *gl->BlitHelper();
1661       gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, fbC->mFB);
1662       blitHelper.DrawBlitTextureToFramebuffer(fbB->ColorTex(), sizeBC, sizeBC);
1663     }
1664 
1665     {
1666       const gl::ScopedBindFramebuffer bindFb(gl);
1667       gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, fbC->mFB);
1668 
1669       if (srcColorFormat->isSRGB) {
1670         // srgb -> linear
1671         gl->fBlitFramebuffer(srcP0.x, srcP0.y, srcP1.x, srcP1.y, dstP0.x,
1672                              dstP0.y, dstP1.x, dstP1.y,
1673                              LOCAL_GL_COLOR_BUFFER_BIT, filter);
1674       } else {
1675         // linear -> srgb
1676         gl->fBlitFramebuffer(dstP0.x, dstP0.y, dstP1.x, dstP1.y, dstP0.x,
1677                              dstP0.y, dstP1.x, dstP1.y,
1678                              LOCAL_GL_COLOR_BUFFER_BIT, LOCAL_GL_NEAREST);
1679       }
1680     }
1681   }
1682 
1683   // -
1684   // glBlitFramebuffer ignores glColorMask!
1685 
1686   if (!webgl->mBoundDrawFramebuffer && webgl->mNeedsFakeNoAlpha) {
1687     if (!webgl->mScissorTestEnabled) {
1688       gl->fEnable(LOCAL_GL_SCISSOR_TEST);
1689     }
1690     if (webgl->mRasterizerDiscardEnabled) {
1691       gl->fDisable(LOCAL_GL_RASTERIZER_DISCARD);
1692     }
1693 
1694     const auto dstRectMin = MinExtents(dstP0, dstP1);
1695     const auto dstRectMax = MaxExtents(dstP0, dstP1);
1696     const auto dstRectSize = dstRectMax - dstRectMin;
1697     const WebGLContext::ScissorRect dstRect = {dstRectMin.x, dstRectMin.y,
1698                                                dstRectSize.x, dstRectSize.y};
1699     dstRect.Apply(*gl);
1700 
1701     gl->fClearColor(0, 0, 0, 1);
1702     webgl->DoColorMask(1 << 3);
1703     gl->fClear(LOCAL_GL_COLOR_BUFFER_BIT);
1704 
1705     if (!webgl->mScissorTestEnabled) {
1706       gl->fDisable(LOCAL_GL_SCISSOR_TEST);
1707     }
1708     if (webgl->mRasterizerDiscardEnabled) {
1709       gl->fEnable(LOCAL_GL_RASTERIZER_DISCARD);
1710     }
1711     webgl->mScissorRect.Apply(*gl);
1712     gl->fClearColor(webgl->mColorClearValue[0], webgl->mColorClearValue[1],
1713                     webgl->mColorClearValue[2], webgl->mColorClearValue[3]);
1714   }
1715 }
1716 
1717 }  // namespace mozilla
1718