tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

WebGLFramebuffer.cpp (55068B)


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