tor-browser

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

WebGLContextGL.cpp (46668B)


      1 /* -*- Mode: C++; tab-width: 4; 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 <fmt/format.h>
      7 
      8 #include <algorithm>
      9 
     10 #include "CanvasUtils.h"
     11 #include "GLContext.h"
     12 #include "MozFramebuffer.h"
     13 #include "WebGL2Context.h"
     14 #include "WebGLBuffer.h"
     15 #include "WebGLContext.h"
     16 #include "WebGLContextUtils.h"
     17 #include "WebGLFormats.h"
     18 #include "WebGLFramebuffer.h"
     19 #include "WebGLProgram.h"
     20 #include "WebGLQuery.h"
     21 #include "WebGLRenderbuffer.h"
     22 #include "WebGLShader.h"
     23 #include "WebGLTexelConversions.h"
     24 #include "WebGLTexture.h"
     25 #include "WebGLValidateStrings.h"
     26 #include "WebGLVertexArray.h"
     27 #include "gfxContext.h"
     28 #include "gfxPlatform.h"
     29 #include "gfxUtils.h"
     30 #include "jsfriendapi.h"
     31 #include "mozilla/RefPtr.h"
     32 #include "mozilla/StaticPrefs_webgl.h"
     33 #include "mozilla/dom/BindingUtils.h"
     34 #include "mozilla/dom/ImageData.h"
     35 #include "mozilla/dom/WebGLRenderingContextBinding.h"
     36 #include "nsContentUtils.h"
     37 #include "nsDebug.h"
     38 #include "nsError.h"
     39 #include "nsLayoutUtils.h"
     40 #include "nsReadableUtils.h"
     41 #include "nsString.h"
     42 
     43 namespace mozilla {
     44 
     45 using namespace mozilla::dom;
     46 using namespace mozilla::gfx;
     47 using namespace mozilla::gl;
     48 
     49 //
     50 //  WebGL API
     51 //
     52 
     53 void WebGLContext::ActiveTexture(uint32_t texUnit) {
     54  FuncScope funcScope(*this, "activeTexture");
     55  if (IsContextLost()) return;
     56  funcScope.mBindFailureGuard = true;
     57 
     58  if (texUnit >= Limits().maxTexUnits) {
     59    return ErrorInvalidEnum("Texture unit %u out of range (%u).", texUnit,
     60                            Limits().maxTexUnits);
     61  }
     62 
     63  mActiveTexture = texUnit;
     64  gl->fActiveTexture(LOCAL_GL_TEXTURE0 + texUnit);
     65 
     66  funcScope.mBindFailureGuard = false;
     67 }
     68 
     69 void WebGLContext::AttachShader(WebGLProgram& prog, WebGLShader& shader) {
     70  FuncScope funcScope(*this, "attachShader");
     71  if (IsContextLost()) return;
     72  funcScope.mBindFailureGuard = true;
     73 
     74  prog.AttachShader(shader);
     75 
     76  funcScope.mBindFailureGuard = false;
     77 }
     78 
     79 void WebGLContext::BindAttribLocation(WebGLProgram& prog, GLuint location,
     80                                      const std::string& name) const {
     81  const FuncScope funcScope(*this, "bindAttribLocation");
     82  if (IsContextLost()) return;
     83 
     84  prog.BindAttribLocation(location, name);
     85 }
     86 
     87 void WebGLContext::BindFramebuffer(GLenum target, WebGLFramebuffer* wfb) {
     88  FuncScope funcScope(*this, "bindFramebuffer");
     89  if (IsContextLost()) return;
     90  funcScope.mBindFailureGuard = true;
     91 
     92  if (!ValidateFramebufferTarget(target)) return;
     93 
     94  if (!wfb) {
     95    gl->fBindFramebuffer(target, 0);
     96  } else {
     97    GLuint framebuffername = wfb->mGLName;
     98    gl->fBindFramebuffer(target, framebuffername);
     99    wfb->mHasBeenBound = true;
    100  }
    101 
    102  switch (target) {
    103    case LOCAL_GL_FRAMEBUFFER:
    104      mBoundDrawFramebuffer = wfb;
    105      mBoundReadFramebuffer = wfb;
    106      break;
    107    case LOCAL_GL_DRAW_FRAMEBUFFER:
    108      mBoundDrawFramebuffer = wfb;
    109      break;
    110    case LOCAL_GL_READ_FRAMEBUFFER:
    111      mBoundReadFramebuffer = wfb;
    112      break;
    113    default:
    114      return;
    115  }
    116  funcScope.mBindFailureGuard = false;
    117 }
    118 
    119 void WebGLContext::BlendEquationSeparate(Maybe<GLuint> i, GLenum modeRGB,
    120                                         GLenum modeAlpha) {
    121  const FuncScope funcScope(*this, "blendEquationSeparate");
    122  if (IsContextLost()) return;
    123 
    124  if (!ValidateBlendEquationEnum(modeRGB, "modeRGB") ||
    125      !ValidateBlendEquationEnum(modeAlpha, "modeAlpha")) {
    126    return;
    127  }
    128 
    129  if (i) {
    130    MOZ_RELEASE_ASSERT(
    131        IsExtensionEnabled(WebGLExtensionID::OES_draw_buffers_indexed));
    132    const auto limit = MaxValidDrawBuffers();
    133    if (*i >= limit) {
    134      ErrorInvalidValue("`index` (%u) must be < %s (%u)", *i,
    135                        "MAX_DRAW_BUFFERS", limit);
    136      return;
    137    }
    138 
    139    gl->fBlendEquationSeparatei(*i, modeRGB, modeAlpha);
    140  } else {
    141    gl->fBlendEquationSeparate(modeRGB, modeAlpha);
    142  }
    143 }
    144 
    145 static bool ValidateBlendFuncEnum(WebGLContext* webgl, GLenum factor,
    146                                  const char* varName) {
    147  switch (factor) {
    148    case LOCAL_GL_ZERO:
    149    case LOCAL_GL_ONE:
    150    case LOCAL_GL_SRC_COLOR:
    151    case LOCAL_GL_ONE_MINUS_SRC_COLOR:
    152    case LOCAL_GL_DST_COLOR:
    153    case LOCAL_GL_ONE_MINUS_DST_COLOR:
    154    case LOCAL_GL_SRC_ALPHA:
    155    case LOCAL_GL_ONE_MINUS_SRC_ALPHA:
    156    case LOCAL_GL_DST_ALPHA:
    157    case LOCAL_GL_ONE_MINUS_DST_ALPHA:
    158    case LOCAL_GL_CONSTANT_COLOR:
    159    case LOCAL_GL_ONE_MINUS_CONSTANT_COLOR:
    160    case LOCAL_GL_CONSTANT_ALPHA:
    161    case LOCAL_GL_ONE_MINUS_CONSTANT_ALPHA:
    162    case LOCAL_GL_SRC_ALPHA_SATURATE:
    163      return true;
    164 
    165    default:
    166      webgl->ErrorInvalidEnumInfo(varName, factor);
    167      return false;
    168  }
    169 }
    170 
    171 static bool ValidateBlendFuncEnums(WebGLContext* webgl, GLenum srcRGB,
    172                                   GLenum srcAlpha, GLenum dstRGB,
    173                                   GLenum dstAlpha) {
    174  if (!webgl->IsWebGL2()) {
    175    if (dstRGB == LOCAL_GL_SRC_ALPHA_SATURATE ||
    176        dstAlpha == LOCAL_GL_SRC_ALPHA_SATURATE) {
    177      webgl->ErrorInvalidEnum(
    178          "LOCAL_GL_SRC_ALPHA_SATURATE as a destination"
    179          " blend function is disallowed in WebGL 1 (dstRGB ="
    180          " 0x%04x, dstAlpha = 0x%04x).",
    181          dstRGB, dstAlpha);
    182      return false;
    183    }
    184  }
    185 
    186  if (!ValidateBlendFuncEnum(webgl, srcRGB, "srcRGB") ||
    187      !ValidateBlendFuncEnum(webgl, srcAlpha, "srcAlpha") ||
    188      !ValidateBlendFuncEnum(webgl, dstRGB, "dstRGB") ||
    189      !ValidateBlendFuncEnum(webgl, dstAlpha, "dstAlpha")) {
    190    return false;
    191  }
    192 
    193  return true;
    194 }
    195 
    196 void WebGLContext::BlendFuncSeparate(Maybe<GLuint> i, GLenum srcRGB,
    197                                     GLenum dstRGB, GLenum srcAlpha,
    198                                     GLenum dstAlpha) {
    199  const FuncScope funcScope(*this, "blendFuncSeparate");
    200  if (IsContextLost()) return;
    201 
    202  if (!ValidateBlendFuncEnums(this, srcRGB, srcAlpha, dstRGB, dstAlpha)) return;
    203 
    204  // note that we only check compatibity for the RGB enums, no need to for the
    205  // Alpha enums, see "Section 6.8 forgetting to mention alpha factors?" thread
    206  // on the public_webgl mailing list
    207  if (!ValidateBlendFuncEnumsCompatibility(srcRGB, dstRGB, "srcRGB and dstRGB"))
    208    return;
    209 
    210  if (i) {
    211    MOZ_RELEASE_ASSERT(
    212        IsExtensionEnabled(WebGLExtensionID::OES_draw_buffers_indexed));
    213    const auto limit = MaxValidDrawBuffers();
    214    if (*i >= limit) {
    215      ErrorInvalidValue("`index` (%u) must be < %s (%u)", *i,
    216                        "MAX_DRAW_BUFFERS", limit);
    217      return;
    218    }
    219 
    220    gl->fBlendFuncSeparatei(*i, srcRGB, dstRGB, srcAlpha, dstAlpha);
    221  } else {
    222    gl->fBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha);
    223  }
    224 }
    225 
    226 GLenum WebGLContext::CheckFramebufferStatus(GLenum target) {
    227  const FuncScope funcScope(*this, "checkFramebufferStatus");
    228  if (IsContextLost()) return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED;
    229 
    230  if (!ValidateFramebufferTarget(target)) return 0;
    231 
    232  WebGLFramebuffer* fb;
    233  switch (target) {
    234    case LOCAL_GL_FRAMEBUFFER:
    235    case LOCAL_GL_DRAW_FRAMEBUFFER:
    236      fb = mBoundDrawFramebuffer;
    237      break;
    238 
    239    case LOCAL_GL_READ_FRAMEBUFFER:
    240      fb = mBoundReadFramebuffer;
    241      break;
    242 
    243    default:
    244      MOZ_CRASH("GFX: Bad target.");
    245  }
    246 
    247  if (!fb) return LOCAL_GL_FRAMEBUFFER_COMPLETE;
    248 
    249  return fb->CheckFramebufferStatus().get();
    250 }
    251 
    252 RefPtr<WebGLProgram> WebGLContext::CreateProgram() {
    253  const FuncScope funcScope(*this, "createProgram");
    254  if (IsContextLost()) return nullptr;
    255 
    256  return new WebGLProgram(this);
    257 }
    258 
    259 RefPtr<WebGLShader> WebGLContext::CreateShader(GLenum type) {
    260  const FuncScope funcScope(*this, "createShader");
    261  if (IsContextLost()) return nullptr;
    262 
    263  if (type != LOCAL_GL_VERTEX_SHADER && type != LOCAL_GL_FRAGMENT_SHADER) {
    264    ErrorInvalidEnumInfo("type", type);
    265    return nullptr;
    266  }
    267 
    268  return new WebGLShader(this, type);
    269 }
    270 
    271 void WebGLContext::CullFace(GLenum face) {
    272  const FuncScope funcScope(*this, "cullFace");
    273  if (IsContextLost()) return;
    274 
    275  if (!ValidateFaceEnum(face)) return;
    276 
    277  gl->fCullFace(face);
    278 }
    279 
    280 void WebGLContext::DetachShader(WebGLProgram& prog, const WebGLShader& shader) {
    281  FuncScope funcScope(*this, "detachShader");
    282  if (IsContextLost()) return;
    283  funcScope.mBindFailureGuard = true;
    284 
    285  prog.DetachShader(shader);
    286 
    287  funcScope.mBindFailureGuard = false;
    288 }
    289 
    290 static bool ValidateComparisonEnum(WebGLContext& webgl, const GLenum func) {
    291  switch (func) {
    292    case LOCAL_GL_NEVER:
    293    case LOCAL_GL_LESS:
    294    case LOCAL_GL_LEQUAL:
    295    case LOCAL_GL_GREATER:
    296    case LOCAL_GL_GEQUAL:
    297    case LOCAL_GL_EQUAL:
    298    case LOCAL_GL_NOTEQUAL:
    299    case LOCAL_GL_ALWAYS:
    300      return true;
    301 
    302    default:
    303      webgl.ErrorInvalidEnumInfo("func", func);
    304      return false;
    305  }
    306 }
    307 
    308 void WebGLContext::DepthFunc(GLenum func) {
    309  const FuncScope funcScope(*this, "depthFunc");
    310  if (IsContextLost()) return;
    311 
    312  if (!ValidateComparisonEnum(*this, func)) return;
    313 
    314  gl->fDepthFunc(func);
    315 }
    316 
    317 void WebGLContext::DepthRange(GLfloat zNear, GLfloat zFar) {
    318  const FuncScope funcScope(*this, "depthRange");
    319  if (IsContextLost()) return;
    320 
    321  if (zNear > zFar)
    322    return ErrorInvalidOperation(
    323        "the near value is greater than the far value!");
    324 
    325  gl->fDepthRange(zNear, zFar);
    326 }
    327 
    328 // -
    329 
    330 void WebGLContext::FramebufferAttach(const GLenum target,
    331                                     const GLenum attachSlot,
    332                                     const GLenum bindImageTarget,
    333                                     const webgl::FbAttachInfo& toAttach) {
    334  FuncScope funcScope(*this, "framebufferAttach");
    335  funcScope.mBindFailureGuard = true;
    336  const auto& limits = *mLimits;
    337 
    338  if (!ValidateFramebufferTarget(target)) return;
    339 
    340  auto fb = mBoundDrawFramebuffer;
    341  if (target == LOCAL_GL_READ_FRAMEBUFFER) {
    342    fb = mBoundReadFramebuffer;
    343  }
    344  if (!fb) return;
    345 
    346  // `rb` needs no validation.
    347 
    348  // `tex`
    349  const auto& tex = toAttach.tex;
    350  if (tex) {
    351    const auto err = CheckFramebufferAttach(bindImageTarget, tex->mTarget.get(),
    352                                            toAttach.mipLevel, toAttach.zLayer,
    353                                            toAttach.zLayerCount, limits);
    354    if (err) return;
    355  }
    356 
    357  auto safeToAttach = toAttach;
    358  if (!toAttach.rb && !toAttach.tex) {
    359    safeToAttach = {};
    360  }
    361  if (!IsWebGL2() &&
    362      !IsExtensionEnabled(WebGLExtensionID::OES_fbo_render_mipmap)) {
    363    safeToAttach.mipLevel = 0;
    364  }
    365  if (!IsExtensionEnabled(WebGLExtensionID::OVR_multiview2)) {
    366    safeToAttach.isMultiview = false;
    367  }
    368 
    369  if (!fb->FramebufferAttach(attachSlot, safeToAttach)) return;
    370 
    371  funcScope.mBindFailureGuard = false;
    372 }
    373 
    374 // -
    375 
    376 void WebGLContext::FrontFace(GLenum mode) {
    377  const FuncScope funcScope(*this, "frontFace");
    378  if (IsContextLost()) return;
    379 
    380  switch (mode) {
    381    case LOCAL_GL_CW:
    382    case LOCAL_GL_CCW:
    383      break;
    384    default:
    385      return ErrorInvalidEnumInfo("mode", mode);
    386  }
    387 
    388  gl->fFrontFace(mode);
    389 }
    390 
    391 Maybe<double> WebGLContext::GetBufferParameter(GLenum target, GLenum pname) {
    392  const FuncScope funcScope(*this, "getBufferParameter");
    393  if (IsContextLost()) return Nothing();
    394 
    395  const auto& slot = ValidateBufferSlot(target);
    396  if (!slot) return Nothing();
    397  const auto& buffer = *slot;
    398 
    399  if (!buffer) {
    400    ErrorInvalidOperation("Buffer for `target` is null.");
    401    return Nothing();
    402  }
    403 
    404  switch (pname) {
    405    case LOCAL_GL_BUFFER_SIZE:
    406      return Some(buffer->ByteLength());
    407 
    408    case LOCAL_GL_BUFFER_USAGE:
    409      return Some(buffer->Usage());
    410 
    411    default:
    412      ErrorInvalidEnumInfo("pname", pname);
    413      return Nothing();
    414  }
    415 }
    416 
    417 Maybe<double> WebGLContext::GetFramebufferAttachmentParameter(
    418    WebGLFramebuffer* const fb, GLenum attachment, GLenum pname) const {
    419  const FuncScope funcScope(*this, "getFramebufferAttachmentParameter");
    420  if (IsContextLost()) return Nothing();
    421 
    422  if (fb) return fb->GetAttachmentParameter(attachment, pname);
    423 
    424  ////////////////////////////////////
    425 
    426  if (!IsWebGL2()) {
    427    ErrorInvalidOperation(
    428        "Querying against the default framebuffer is not"
    429        " allowed in WebGL 1.");
    430    return Nothing();
    431  }
    432 
    433  switch (attachment) {
    434    case LOCAL_GL_BACK:
    435    case LOCAL_GL_DEPTH:
    436    case LOCAL_GL_STENCIL:
    437      break;
    438 
    439    default:
    440      ErrorInvalidEnum(
    441          "For the default framebuffer, can only query COLOR, DEPTH,"
    442          " or STENCIL.");
    443      return Nothing();
    444  }
    445 
    446  switch (pname) {
    447    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
    448      switch (attachment) {
    449        case LOCAL_GL_BACK:
    450          break;
    451        case LOCAL_GL_DEPTH:
    452          if (!mOptions.depth) {
    453            return Some(LOCAL_GL_NONE);
    454          }
    455          break;
    456        case LOCAL_GL_STENCIL:
    457          if (!mOptions.stencil) {
    458            return Some(LOCAL_GL_NONE);
    459          }
    460          break;
    461        default:
    462          ErrorInvalidEnum(
    463              "With the default framebuffer, can only query COLOR, DEPTH,"
    464              " or STENCIL for GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE");
    465          return Nothing();
    466      }
    467      return Some(LOCAL_GL_FRAMEBUFFER_DEFAULT);
    468 
    469      ////////////////
    470 
    471    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE:
    472    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE:
    473    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE:
    474      if (attachment == LOCAL_GL_BACK) return Some(8);
    475      return Some(0);
    476 
    477    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE:
    478      if (attachment == LOCAL_GL_BACK) {
    479        if (mOptions.alpha) {
    480          return Some(8);
    481        }
    482        ErrorInvalidOperation(
    483            "The default framebuffer doesn't contain an alpha buffer");
    484        return Nothing();
    485      }
    486      return Some(0);
    487 
    488    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE:
    489      if (attachment == LOCAL_GL_DEPTH) {
    490        if (mOptions.depth) {
    491          return Some(24);
    492        }
    493        ErrorInvalidOperation(
    494            "The default framebuffer doesn't contain an depth buffer");
    495        return Nothing();
    496      }
    497      return Some(0);
    498 
    499    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE:
    500      if (attachment == LOCAL_GL_STENCIL) {
    501        if (mOptions.stencil) {
    502          return Some(8);
    503        }
    504        ErrorInvalidOperation(
    505            "The default framebuffer doesn't contain an stencil buffer");
    506        return Nothing();
    507      }
    508      return Some(0);
    509 
    510    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE:
    511      if (attachment == LOCAL_GL_STENCIL) {
    512        if (mOptions.stencil) {
    513          return Some(LOCAL_GL_UNSIGNED_INT);
    514        }
    515        ErrorInvalidOperation(
    516            "The default framebuffer doesn't contain an stencil buffer");
    517      } else if (attachment == LOCAL_GL_DEPTH) {
    518        if (mOptions.depth) {
    519          return Some(LOCAL_GL_UNSIGNED_NORMALIZED);
    520        }
    521        ErrorInvalidOperation(
    522            "The default framebuffer doesn't contain an depth buffer");
    523      } else {  // LOCAL_GL_BACK
    524        return Some(LOCAL_GL_UNSIGNED_NORMALIZED);
    525      }
    526      return Nothing();
    527 
    528    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING:
    529      if (attachment == LOCAL_GL_STENCIL) {
    530        if (!mOptions.stencil) {
    531          ErrorInvalidOperation(
    532              "The default framebuffer doesn't contain an stencil buffer");
    533          return Nothing();
    534        }
    535      } else if (attachment == LOCAL_GL_DEPTH) {
    536        if (!mOptions.depth) {
    537          ErrorInvalidOperation(
    538              "The default framebuffer doesn't contain an depth buffer");
    539          return Nothing();
    540        }
    541      }
    542      return Some(LOCAL_GL_LINEAR);
    543  }
    544 
    545  ErrorInvalidEnumInfo("pname", pname);
    546  return Nothing();
    547 }
    548 
    549 Maybe<double> WebGLContext::GetRenderbufferParameter(
    550    const WebGLRenderbuffer& rb, GLenum pname) const {
    551  const FuncScope funcScope(*this, "getRenderbufferParameter");
    552  if (IsContextLost()) return Nothing();
    553 
    554  switch (pname) {
    555    case LOCAL_GL_RENDERBUFFER_SAMPLES:
    556      if (!IsWebGL2()) break;
    557      [[fallthrough]];
    558 
    559    case LOCAL_GL_RENDERBUFFER_WIDTH:
    560    case LOCAL_GL_RENDERBUFFER_HEIGHT:
    561    case LOCAL_GL_RENDERBUFFER_RED_SIZE:
    562    case LOCAL_GL_RENDERBUFFER_GREEN_SIZE:
    563    case LOCAL_GL_RENDERBUFFER_BLUE_SIZE:
    564    case LOCAL_GL_RENDERBUFFER_ALPHA_SIZE:
    565    case LOCAL_GL_RENDERBUFFER_DEPTH_SIZE:
    566    case LOCAL_GL_RENDERBUFFER_STENCIL_SIZE:
    567    case LOCAL_GL_RENDERBUFFER_INTERNAL_FORMAT: {
    568      // RB emulation means we have to ask the RB itself.
    569      GLint i = rb.GetRenderbufferParameter(pname);
    570      return Some(i);
    571    }
    572 
    573    default:
    574      break;
    575  }
    576 
    577  ErrorInvalidEnumInfo("pname", pname);
    578  return Nothing();
    579 }
    580 
    581 RefPtr<WebGLTexture> WebGLContext::CreateTexture() {
    582  const FuncScope funcScope(*this, "createTexture");
    583  if (IsContextLost()) return nullptr;
    584 
    585  GLuint tex = 0;
    586  gl->fGenTextures(1, &tex);
    587 
    588  return new WebGLTexture(this, tex);
    589 }
    590 
    591 GLenum WebGLContext::GetError() {
    592  const FuncScope funcScope(*this, "getError");
    593 
    594  /* WebGL 1.0: Section 5.14.3: Setting and getting state:
    595   *   If the context's webgl context lost flag is set, returns
    596   *   CONTEXT_LOST_WEBGL the first time this method is called.
    597   *   Afterward, returns NO_ERROR until the context has been
    598   *   restored.
    599   *
    600   * WEBGL_lose_context:
    601   *   [When this extension is enabled: ] loseContext and
    602   *   restoreContext are allowed to generate INVALID_OPERATION errors
    603   *   even when the context is lost.
    604   */
    605 
    606  auto err = mWebGLError;
    607  mWebGLError = 0;
    608  if (IsContextLost() || err)  // Must check IsContextLost in all flow paths.
    609    return err;
    610 
    611  // Either no WebGL-side error, or it's already been cleared.
    612  // UnderlyingGL-side errors, now.
    613  err = gl->fGetError();
    614  if (gl->IsContextLost()) {
    615    CheckForContextLoss();
    616    return GetError();
    617  }
    618  MOZ_ASSERT(err != LOCAL_GL_CONTEXT_LOST);
    619 
    620  if (err) {
    621    GenerateWarning("Driver error unexpected by WebGL: 0x%04x", err);
    622    // This might be:
    623    // - INVALID_OPERATION from ANGLE due to incomplete RBAB implementation for
    624    // DrawElements
    625    //   with DYNAMIC_DRAW index buffer.
    626  }
    627  return err;
    628 }
    629 
    630 webgl::GetUniformData WebGLContext::GetUniform(const WebGLProgram& prog,
    631                                               const uint32_t loc) const {
    632  const FuncScope funcScope(*this, "getUniform");
    633  webgl::GetUniformData ret;
    634  [&]() {
    635    if (IsContextLost()) return;
    636 
    637    const auto& info = prog.LinkInfo();
    638    if (!info) return;
    639 
    640    const auto locInfo = MaybeFind(info->locationMap, loc);
    641    if (!locInfo) return;
    642 
    643    ret.type = locInfo->info.info.elemType;
    644    switch (ret.type) {
    645      case LOCAL_GL_FLOAT:
    646      case LOCAL_GL_FLOAT_VEC2:
    647      case LOCAL_GL_FLOAT_VEC3:
    648      case LOCAL_GL_FLOAT_VEC4:
    649      case LOCAL_GL_FLOAT_MAT2:
    650      case LOCAL_GL_FLOAT_MAT3:
    651      case LOCAL_GL_FLOAT_MAT4:
    652      case LOCAL_GL_FLOAT_MAT2x3:
    653      case LOCAL_GL_FLOAT_MAT2x4:
    654      case LOCAL_GL_FLOAT_MAT3x2:
    655      case LOCAL_GL_FLOAT_MAT3x4:
    656      case LOCAL_GL_FLOAT_MAT4x2:
    657      case LOCAL_GL_FLOAT_MAT4x3:
    658        gl->fGetUniformfv(prog.mGLName, loc,
    659                          reinterpret_cast<float*>(ret.data));
    660        break;
    661 
    662      case LOCAL_GL_INT:
    663      case LOCAL_GL_INT_VEC2:
    664      case LOCAL_GL_INT_VEC3:
    665      case LOCAL_GL_INT_VEC4:
    666      case LOCAL_GL_SAMPLER_2D:
    667      case LOCAL_GL_SAMPLER_3D:
    668      case LOCAL_GL_SAMPLER_CUBE:
    669      case LOCAL_GL_SAMPLER_2D_SHADOW:
    670      case LOCAL_GL_SAMPLER_2D_ARRAY:
    671      case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
    672      case LOCAL_GL_SAMPLER_CUBE_SHADOW:
    673      case LOCAL_GL_INT_SAMPLER_2D:
    674      case LOCAL_GL_INT_SAMPLER_3D:
    675      case LOCAL_GL_INT_SAMPLER_CUBE:
    676      case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
    677      case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
    678      case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
    679      case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
    680      case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
    681      case LOCAL_GL_BOOL:
    682      case LOCAL_GL_BOOL_VEC2:
    683      case LOCAL_GL_BOOL_VEC3:
    684      case LOCAL_GL_BOOL_VEC4:
    685        gl->fGetUniformiv(prog.mGLName, loc,
    686                          reinterpret_cast<int32_t*>(ret.data));
    687        break;
    688 
    689      case LOCAL_GL_UNSIGNED_INT:
    690      case LOCAL_GL_UNSIGNED_INT_VEC2:
    691      case LOCAL_GL_UNSIGNED_INT_VEC3:
    692      case LOCAL_GL_UNSIGNED_INT_VEC4:
    693        gl->fGetUniformuiv(prog.mGLName, loc,
    694                           reinterpret_cast<uint32_t*>(ret.data));
    695        break;
    696 
    697      default:
    698        MOZ_CRASH("GFX: Invalid elemType.");
    699    }
    700  }();
    701  return ret;
    702 }
    703 
    704 void WebGLContext::Hint(GLenum target, GLenum mode) {
    705  const FuncScope funcScope(*this, "hint");
    706  if (IsContextLost()) return;
    707 
    708  switch (mode) {
    709    case LOCAL_GL_FASTEST:
    710    case LOCAL_GL_NICEST:
    711    case LOCAL_GL_DONT_CARE:
    712      break;
    713    default:
    714      return ErrorInvalidEnumArg("mode", mode);
    715  }
    716 
    717  // -
    718 
    719  bool isValid = false;
    720 
    721  switch (target) {
    722    case LOCAL_GL_GENERATE_MIPMAP_HINT:
    723      mGenerateMipmapHint = mode;
    724      isValid = true;
    725 
    726      // Deprecated and removed in desktop GL Core profiles.
    727      if (gl->IsCoreProfile()) return;
    728 
    729      break;
    730 
    731    case LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT:
    732      if (IsWebGL2() ||
    733          IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives)) {
    734        isValid = true;
    735      }
    736      break;
    737  }
    738  if (!isValid) return ErrorInvalidEnumInfo("target", target);
    739 
    740  // -
    741 
    742  gl->fHint(target, mode);
    743 }
    744 
    745 // -
    746 
    747 void WebGLContext::LinkProgram(WebGLProgram& prog) {
    748  const FuncScope funcScope(*this, "linkProgram");
    749  if (IsContextLost()) return;
    750 
    751  prog.LinkProgram();
    752 
    753  if (&prog == mCurrentProgram) {
    754    if (!prog.IsLinked()) {
    755      // We use to simply early-out here, and preserve the GL behavior that
    756      // failed relink doesn't invalidate the current active program link info.
    757      // The new behavior was changed for WebGL here:
    758      // https://github.com/KhronosGroup/WebGL/pull/3371
    759      mActiveProgramLinkInfo = nullptr;
    760      gl->fUseProgram(0);  // Shouldn't be needed, but let's be safe.
    761      return;
    762    }
    763    mActiveProgramLinkInfo = prog.LinkInfo();
    764    gl->fUseProgram(prog.mGLName);  // Uncontionally re-use.
    765    // Previously, we needed this re-use on nvidia as a driver workaround,
    766    // but we might as well do it unconditionally.
    767  }
    768 }
    769 
    770 Maybe<webgl::ErrorInfo> SetPixelUnpack(
    771    const bool isWebgl2, webgl::PixelUnpackStateWebgl* const unpacking,
    772    const GLenum pname, const GLint param) {
    773  if (isWebgl2) {
    774    uint32_t* pValueSlot = nullptr;
    775    switch (pname) {
    776      case LOCAL_GL_UNPACK_IMAGE_HEIGHT:
    777        pValueSlot = &unpacking->imageHeight;
    778        break;
    779 
    780      case LOCAL_GL_UNPACK_SKIP_IMAGES:
    781        pValueSlot = &unpacking->skipImages;
    782        break;
    783 
    784      case LOCAL_GL_UNPACK_ROW_LENGTH:
    785        pValueSlot = &unpacking->rowLength;
    786        break;
    787 
    788      case LOCAL_GL_UNPACK_SKIP_ROWS:
    789        pValueSlot = &unpacking->skipRows;
    790        break;
    791 
    792      case LOCAL_GL_UNPACK_SKIP_PIXELS:
    793        pValueSlot = &unpacking->skipPixels;
    794        break;
    795    }
    796 
    797    if (pValueSlot) {
    798      *pValueSlot = static_cast<uint32_t>(param);
    799      return {};
    800    }
    801  }
    802 
    803  switch (pname) {
    804    case dom::WebGLRenderingContext_Binding::UNPACK_FLIP_Y_WEBGL:
    805      unpacking->flipY = bool(param);
    806      return {};
    807 
    808    case dom::WebGLRenderingContext_Binding::UNPACK_PREMULTIPLY_ALPHA_WEBGL:
    809      unpacking->premultiplyAlpha = bool(param);
    810      return {};
    811 
    812    case dom::WebGLRenderingContext_Binding::UNPACK_COLORSPACE_CONVERSION_WEBGL:
    813      switch (param) {
    814        case LOCAL_GL_NONE:
    815        case dom::WebGLRenderingContext_Binding::BROWSER_DEFAULT_WEBGL:
    816          break;
    817 
    818        default: {
    819          const nsPrintfCString text("Bad UNPACK_COLORSPACE_CONVERSION: %s",
    820                                     EnumString(param).c_str());
    821          return Some(webgl::ErrorInfo{LOCAL_GL_INVALID_VALUE, ToString(text)});
    822        }
    823      }
    824      unpacking->colorspaceConversion = param;
    825      return {};
    826 
    827    case dom::MOZ_debug_Binding::UNPACK_REQUIRE_FASTPATH:
    828      unpacking->requireFastPath = bool(param);
    829      return {};
    830 
    831    case LOCAL_GL_UNPACK_ALIGNMENT:
    832      switch (param) {
    833        case 1:
    834        case 2:
    835        case 4:
    836        case 8:
    837          break;
    838 
    839        default: {
    840          const nsPrintfCString text(
    841              "UNPACK_ALIGNMENT must be [1,2,4,8], was %i", param);
    842          return Some(webgl::ErrorInfo{LOCAL_GL_INVALID_VALUE, ToString(text)});
    843        }
    844      }
    845      unpacking->alignmentInTypeElems = param;
    846      return {};
    847 
    848    default:
    849      break;
    850  }
    851  const nsPrintfCString text("Bad `pname`: %s", EnumString(pname).c_str());
    852  return Some(webgl::ErrorInfo{LOCAL_GL_INVALID_ENUM, ToString(text)});
    853 }
    854 
    855 bool WebGLContext::DoReadPixelsAndConvert(
    856    const webgl::FormatInfo* const srcFormat, const webgl::ReadPixelsDesc& desc,
    857    const uintptr_t dest, const uint64_t destSize, const uint32_t rowStride) {
    858  const auto& x = desc.srcOffset.x;
    859  const auto& y = desc.srcOffset.y;
    860  const auto size = *ivec2::From(desc.size);
    861  const auto& pi = desc.pi;
    862 
    863  // On at least Win+NV, we'll get PBO errors if we don't have at least
    864  // `rowStride * height` bytes available to read into.
    865  const auto naiveBytesNeeded = CheckedInt<uint64_t>(rowStride) * size.y;
    866  const bool isDangerCloseToEdge =
    867      (!naiveBytesNeeded.isValid() || naiveBytesNeeded.value() > destSize);
    868  const bool useParanoidHandling =
    869      (gl->WorkAroundDriverBugs() && isDangerCloseToEdge &&
    870       mBoundPixelPackBuffer);
    871  if (!useParanoidHandling) {
    872    gl->fReadPixels(x, y, size.x, size.y, pi.format, pi.type,
    873                    reinterpret_cast<void*>(dest));
    874    return true;
    875  }
    876 
    877  // Read everything but the last row.
    878  const auto bodyHeight = size.y - 1;
    879  if (bodyHeight) {
    880    gl->fReadPixels(x, y, size.x, bodyHeight, pi.format, pi.type,
    881                    reinterpret_cast<void*>(dest));
    882  }
    883 
    884  // Now read the last row.
    885  gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, 1);
    886  gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, 0);
    887  gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, 0);
    888 
    889  const auto tailRowOffset =
    890      reinterpret_cast<uint8_t*>(dest) + rowStride * bodyHeight;
    891  gl->fReadPixels(x, y + bodyHeight, size.x, 1, pi.format, pi.type,
    892                  tailRowOffset);
    893 
    894  return true;
    895 }
    896 
    897 webgl::ReadPixelsResult WebGLContext::ReadPixelsInto(
    898    const webgl::ReadPixelsDesc& desc, const Range<uint8_t>& dest) {
    899  const FuncScope funcScope(*this, "readPixels");
    900  if (IsContextLost()) return {};
    901 
    902  if (mBoundPixelPackBuffer) {
    903    ErrorInvalidOperation("PIXEL_PACK_BUFFER must be null.");
    904    return {};
    905  }
    906 
    907  return ReadPixelsImpl(desc, reinterpret_cast<uintptr_t>(dest.begin().get()),
    908                        dest.length());
    909 }
    910 
    911 void WebGLContext::ReadPixelsPbo(const webgl::ReadPixelsDesc& desc,
    912                                 const uint64_t offset) {
    913  const FuncScope funcScope(*this, "readPixels");
    914  if (IsContextLost()) return;
    915 
    916  const auto& buffer = ValidateBufferSelection(LOCAL_GL_PIXEL_PACK_BUFFER);
    917  if (!buffer) return;
    918 
    919  //////
    920 
    921  {
    922    const auto pii = webgl::PackingInfoInfo::For(desc.pi);
    923    if (!pii) {
    924      GLenum err = LOCAL_GL_INVALID_OPERATION;
    925      if (!desc.pi.format || !desc.pi.type) {
    926        err = LOCAL_GL_INVALID_ENUM;
    927      }
    928      GenerateError(err, "`format` (%s) and/or `type` (%s) not acceptable.",
    929                    EnumString(desc.pi.format).c_str(),
    930                    EnumString(desc.pi.type).c_str());
    931      return;
    932    }
    933 
    934    if (offset % pii->bytesPerElement != 0) {
    935      ErrorInvalidOperation(
    936          "`offset` must be divisible by the size of `type`"
    937          " in bytes.");
    938      return;
    939    }
    940  }
    941 
    942  //////
    943 
    944  auto bytesAvailable = buffer->ByteLength();
    945  if (offset > bytesAvailable) {
    946    ErrorInvalidOperation("`offset` too large for bound PIXEL_PACK_BUFFER.");
    947    return;
    948  }
    949  bytesAvailable -= offset;
    950 
    951  // -
    952 
    953  const ScopedLazyBind lazyBind(gl, LOCAL_GL_PIXEL_PACK_BUFFER, buffer);
    954 
    955  ReadPixelsImpl(desc, offset, bytesAvailable);
    956 
    957  buffer->ResetLastUpdateFenceId();
    958 }
    959 
    960 static webgl::PackingInfo DefaultReadPixelPI(
    961    const webgl::FormatUsageInfo* usage) {
    962  MOZ_ASSERT(usage->IsRenderable());
    963  const auto& format = *usage->format;
    964  switch (format.componentType) {
    965    case webgl::ComponentType::NormUInt:
    966      if (format.r == 16) {
    967        return {LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_SHORT};
    968      }
    969      return {LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE};
    970 
    971    case webgl::ComponentType::Int:
    972      return {LOCAL_GL_RGBA_INTEGER, LOCAL_GL_INT};
    973 
    974    case webgl::ComponentType::UInt:
    975      return {LOCAL_GL_RGBA_INTEGER, LOCAL_GL_UNSIGNED_INT};
    976 
    977    case webgl::ComponentType::Float:
    978      return {LOCAL_GL_RGBA, LOCAL_GL_FLOAT};
    979 
    980    case webgl::ComponentType::NormInt:
    981      MOZ_RELEASE_ASSERT(false, "SNORM formats are never color-renderable!");
    982      break;
    983  }
    984  MOZ_CRASH("bad webgl::ComponentType");
    985 }
    986 
    987 static bool ArePossiblePackEnums(const webgl::PackingInfo& pi) {
    988  // OpenGL ES 2.0 $4.3.1 - IMPLEMENTATION_COLOR_READ_{TYPE/FORMAT} is a valid
    989  // combination for glReadPixels()...
    990 
    991  // Only valid when pulled from:
    992  // * GLES 2.0.25 p105:
    993  //   "table 3.4, excluding formats LUMINANCE and LUMINANCE_ALPHA."
    994  // * GLES 3.0.4 p193:
    995  //   "table 3.2, excluding formats DEPTH_COMPONENT and DEPTH_STENCIL."
    996  switch (pi.format) {
    997    case LOCAL_GL_LUMINANCE:
    998    case LOCAL_GL_LUMINANCE_ALPHA:
    999    case LOCAL_GL_DEPTH_COMPONENT:
   1000    case LOCAL_GL_DEPTH_STENCIL:
   1001      return false;
   1002  }
   1003 
   1004  if (pi.type == LOCAL_GL_UNSIGNED_INT_24_8) return false;
   1005 
   1006  const auto pii = webgl::PackingInfoInfo::For(pi);
   1007  if (!pii) return false;
   1008 
   1009  return true;
   1010 }
   1011 
   1012 webgl::PackingInfo WebGLContext::ValidImplementationColorReadPI(
   1013    const webgl::FormatUsageInfo* usage) const {
   1014  if (const auto implPI = usage->implReadPiCache) return *implPI;
   1015 
   1016  const auto defaultPI = DefaultReadPixelPI(usage);
   1017  usage->implReadPiCache = [&]() {
   1018    if (StaticPrefs::webgl_porting_strict_readpixels_formats())
   1019      return defaultPI;
   1020    auto implPI = defaultPI;
   1021    // ES2_compatibility always returns RGBA/UNSIGNED_BYTE, so branch on actual
   1022    // IsGLES(). Also OSX+NV generates an error here.
   1023    if (gl->IsGLES()) {
   1024      gl->GetInt(LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT, &implPI.format);
   1025      gl->GetInt(LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE, &implPI.type);
   1026    } else {
   1027      if (StaticPrefs::webgl_porting_strict_readpixels_formats_non_es())
   1028        return defaultPI;
   1029      // Non-ES GL is way more open, and basically supports reading anything.
   1030      // (Sucks to be their driver team!)
   1031      if (usage->idealUnpack) {
   1032        for (const auto& [validPi, validDui] : usage->validUnpacks) {
   1033          if (validDui != *usage->idealUnpack) continue;
   1034          implPI = validPi;
   1035          break;
   1036        }
   1037      }
   1038    }
   1039    // Normalize HALF_FLOAT_OES to HALF_FLOAT internally.
   1040    if (implPI.type == LOCAL_GL_HALF_FLOAT_OES) {
   1041      implPI.type = LOCAL_GL_HALF_FLOAT;
   1042    }
   1043    if (!ArePossiblePackEnums(implPI)) return defaultPI;
   1044    return implPI;
   1045  }();
   1046  return *usage->implReadPiCache;
   1047 }
   1048 
   1049 std::string webgl::format_as(const PackingInfo& pi) {
   1050  return fmt::format(FMT_STRING("{}/{}"), pi.format, pi.type);
   1051 }
   1052 
   1053 static bool ValidateReadPixelsFormatAndType(
   1054    const webgl::FormatUsageInfo* srcUsage, const webgl::PackingInfo& pi,
   1055    WebGLContext* webgl) {
   1056  if (!ArePossiblePackEnums(pi)) {
   1057    webgl->ErrorInvalidEnum("Unexpected format or type.");
   1058    return false;
   1059  }
   1060 
   1061  const auto defaultPI = DefaultReadPixelPI(srcUsage);
   1062  if (pi == defaultPI) return true;
   1063 
   1064  ////
   1065 
   1066  // OpenGL ES 3.0.4 p194 - When the internal format of the rendering surface is
   1067  // RGB10_A2, a third combination of format RGBA and type
   1068  // UNSIGNED_INT_2_10_10_10_REV is accepted.
   1069  std::optional<webgl::PackingInfo> bonusValidPi;
   1070  if (srcUsage->format->effectiveFormat == webgl::EffectiveFormat::RGB10_A2) {
   1071    bonusValidPi =
   1072        webgl::PackingInfo{LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV};
   1073  }
   1074  if (bonusValidPi && pi == *bonusValidPi) return true;
   1075 
   1076  ////
   1077 
   1078  const auto implPI = webgl->ValidImplementationColorReadPI(srcUsage);
   1079  MOZ_ASSERT(pi.type != LOCAL_GL_HALF_FLOAT_OES);      // HALF_FLOAT-only
   1080  MOZ_ASSERT(implPI.type != LOCAL_GL_HALF_FLOAT_OES);  // HALF_FLOAT-only
   1081  if (pi == implPI) return true;
   1082 
   1083  ////
   1084 
   1085  // Map HALF_FLOAT to HALF_FLOAT_OES for error messages in webgl1.
   1086  webgl::PackingInfo clientImplPI = implPI;
   1087  if (clientImplPI.type == LOCAL_GL_HALF_FLOAT && !webgl->IsWebGL2()) {
   1088    clientImplPI.type = LOCAL_GL_HALF_FLOAT_OES;
   1089  }
   1090 
   1091  auto validPiStr =
   1092      fmt::format(FMT_STRING("{} (spec-required baseline for format {})"),
   1093                  defaultPI, srcUsage->format->name);
   1094  if (implPI != defaultPI) {
   1095    validPiStr += fmt::format(
   1096        FMT_STRING(
   1097            ", or {} (spec-optional implementation-chosen format-dependant"
   1098            " IMPLEMENTATION_COLOR_READ_FORMAT/_TYPE)"),
   1099        clientImplPI);
   1100  }
   1101  if (bonusValidPi) {
   1102    validPiStr +=
   1103        fmt::format(FMT_STRING(", or {} (spec-required bonus for format {})"),
   1104                    *bonusValidPi, srcUsage->format->name);
   1105  }
   1106 
   1107  webgl->ErrorInvalidOperation(
   1108      "Format/type %s/%s incompatible with this %s framebuffer. Must use: %s.",
   1109      EnumString(pi.format).c_str(), EnumString(pi.type).c_str(),
   1110      srcUsage->format->name, validPiStr.c_str());
   1111  return false;
   1112 }
   1113 
   1114 webgl::ReadPixelsResult WebGLContext::ReadPixelsImpl(
   1115    const webgl::ReadPixelsDesc& desc, const uintptr_t dest,
   1116    const uint64_t availBytes) {
   1117  const webgl::FormatUsageInfo* srcFormat;
   1118  uint32_t srcWidth;
   1119  uint32_t srcHeight;
   1120  if (!BindCurFBForColorRead(&srcFormat, &srcWidth, &srcHeight)) return {};
   1121 
   1122  //////
   1123 
   1124  if (!ValidateReadPixelsFormatAndType(srcFormat, desc.pi, this)) return {};
   1125 
   1126  //////
   1127 
   1128  const auto& srcOffset = desc.srcOffset;
   1129  const auto& size = desc.size;
   1130 
   1131  if (!ivec2::From(size)) {
   1132    ErrorInvalidValue("width and height must be non-negative.");
   1133    return {};
   1134  }
   1135 
   1136  const auto& packing = desc.packState;
   1137  const auto explicitPackingRes = webgl::ExplicitPixelPackingState::ForUseWith(
   1138      packing, LOCAL_GL_TEXTURE_2D, {size.x, size.y, 1}, desc.pi, {});
   1139  if (!explicitPackingRes.isOk()) {
   1140    ErrorInvalidOperation("%s", explicitPackingRes.inspectErr().c_str());
   1141    return {};
   1142  }
   1143  const auto& explicitPacking = explicitPackingRes.inspect();
   1144  const auto& rowStride = explicitPacking.metrics.bytesPerRowStride;
   1145  const auto& bytesNeeded = explicitPacking.metrics.totalBytesUsed;
   1146  if (bytesNeeded > availBytes) {
   1147    ErrorInvalidOperation("buffer too small");
   1148    return {};
   1149  }
   1150 
   1151  ////
   1152 
   1153  int32_t readX, readY;
   1154  int32_t writeX, writeY;
   1155  int32_t rwWidth, rwHeight;
   1156  if (!Intersect(srcWidth, srcOffset.x, size.x, &readX, &writeX, &rwWidth) ||
   1157      !Intersect(srcHeight, srcOffset.y, size.y, &readY, &writeY, &rwHeight)) {
   1158    ErrorOutOfMemory("Bad subrect selection.");
   1159    return {};
   1160  }
   1161 
   1162  ////////////////
   1163  // Now that the errors are out of the way, on to actually reading!
   1164 
   1165  gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, packing.alignmentInTypeElems);
   1166  if (IsWebGL2()) {
   1167    gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, packing.rowLength);
   1168    gl->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, packing.skipPixels);
   1169    gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, packing.skipRows);
   1170  }
   1171 
   1172  if (!rwWidth || !rwHeight) {
   1173    // Disjoint rects, so we're done already.
   1174    DummyReadFramebufferOperation();
   1175    return {};
   1176  }
   1177  const auto rwSize = *uvec2::From(rwWidth, rwHeight);
   1178 
   1179  const auto res = webgl::ReadPixelsResult{
   1180      {{writeX, writeY}, {rwSize.x, rwSize.y}}, rowStride};
   1181 
   1182  if (rwSize == size) {
   1183    DoReadPixelsAndConvert(srcFormat->format, desc, dest, bytesNeeded,
   1184                           rowStride);
   1185    return res;
   1186  }
   1187 
   1188  // Read request contains out-of-bounds pixels. Unfortunately:
   1189  // GLES 3.0.4 p194 "Obtaining Pixels from the Framebuffer":
   1190  // "If any of these pixels lies outside of the window allocated to the current
   1191  // GL context, or outside of the image attached to the currently bound
   1192  // framebuffer object, then the values obtained for those pixels are
   1193  // undefined."
   1194 
   1195  // This is a slow-path, so warn people away!
   1196  GenerateWarning(
   1197      "Out-of-bounds reads with readPixels are deprecated, and"
   1198      " may be slow.");
   1199 
   1200  ////////////////////////////////////
   1201  // Read only the in-bounds pixels.
   1202 
   1203  if (IsWebGL2()) {
   1204    if (!packing.rowLength) {
   1205      gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, packing.skipPixels + size.x);
   1206    }
   1207    gl->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, packing.skipPixels + writeX);
   1208    gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, packing.skipRows + writeY);
   1209 
   1210    auto desc2 = desc;
   1211    desc2.srcOffset = {readX, readY};
   1212    desc2.size = rwSize;
   1213 
   1214    DoReadPixelsAndConvert(srcFormat->format, desc2, dest, bytesNeeded,
   1215                           rowStride);
   1216  } else {
   1217    // I *did* say "hilariously slow".
   1218 
   1219    auto desc2 = desc;
   1220    desc2.srcOffset = {readX, readY};
   1221    desc2.size = {rwSize.x, 1};
   1222 
   1223    const auto skipBytes = writeX * explicitPacking.metrics.bytesPerPixel;
   1224    const auto usedRowBytes = rwSize.x * explicitPacking.metrics.bytesPerPixel;
   1225    for (const auto j : IntegerRange(rwSize.y)) {
   1226      desc2.srcOffset.y = readY + j;
   1227      const auto destWriteBegin = dest + skipBytes + (writeY + j) * rowStride;
   1228      MOZ_RELEASE_ASSERT(dest <= destWriteBegin);
   1229      MOZ_RELEASE_ASSERT(destWriteBegin <= dest + availBytes);
   1230 
   1231      const auto destWriteEnd = destWriteBegin + usedRowBytes;
   1232      MOZ_RELEASE_ASSERT(dest <= destWriteEnd);
   1233      MOZ_RELEASE_ASSERT(destWriteEnd <= dest + availBytes);
   1234 
   1235      DoReadPixelsAndConvert(srcFormat->format, desc2, destWriteBegin,
   1236                             destWriteEnd - destWriteBegin, rowStride);
   1237    }
   1238  }
   1239 
   1240  return res;
   1241 }
   1242 
   1243 void WebGLContext::RenderbufferStorageMultisample(WebGLRenderbuffer& rb,
   1244                                                  uint32_t samples,
   1245                                                  GLenum internalFormat,
   1246                                                  uint32_t width,
   1247                                                  uint32_t height) const {
   1248  const FuncScope funcScope(*this, "renderbufferStorage(Multisample)?");
   1249  if (IsContextLost()) return;
   1250 
   1251  rb.RenderbufferStorage(samples, internalFormat, width, height);
   1252 }
   1253 
   1254 void WebGLContext::Scissor(GLint x, GLint y, GLsizei width, GLsizei height) {
   1255  const FuncScope funcScope(*this, "scissor");
   1256  if (IsContextLost()) return;
   1257 
   1258  if (!ValidateNonNegative("width", width) ||
   1259      !ValidateNonNegative("height", height)) {
   1260    return;
   1261  }
   1262 
   1263  mScissorRect = {x, y, width, height};
   1264  mScissorRect.Apply(*gl);
   1265 }
   1266 
   1267 void WebGLContext::StencilFuncSeparate(GLenum face, GLenum func, GLint ref,
   1268                                       GLuint mask) {
   1269  const FuncScope funcScope(*this, "stencilFuncSeparate");
   1270  if (IsContextLost()) return;
   1271 
   1272  if (!ValidateFaceEnum(face) || !ValidateComparisonEnum(*this, func)) {
   1273    return;
   1274  }
   1275 
   1276  switch (face) {
   1277    case LOCAL_GL_FRONT_AND_BACK:
   1278      mStencilRefFront = ref;
   1279      mStencilRefBack = ref;
   1280      mStencilValueMaskFront = mask;
   1281      mStencilValueMaskBack = mask;
   1282      break;
   1283    case LOCAL_GL_FRONT:
   1284      mStencilRefFront = ref;
   1285      mStencilValueMaskFront = mask;
   1286      break;
   1287    case LOCAL_GL_BACK:
   1288      mStencilRefBack = ref;
   1289      mStencilValueMaskBack = mask;
   1290      break;
   1291  }
   1292 
   1293  gl->fStencilFuncSeparate(face, func, ref, mask);
   1294 }
   1295 
   1296 void WebGLContext::StencilOpSeparate(GLenum face, GLenum sfail, GLenum dpfail,
   1297                                     GLenum dppass) {
   1298  const FuncScope funcScope(*this, "stencilOpSeparate");
   1299  if (IsContextLost()) return;
   1300 
   1301  if (!ValidateFaceEnum(face) || !ValidateStencilOpEnum(sfail, "sfail") ||
   1302      !ValidateStencilOpEnum(dpfail, "dpfail") ||
   1303      !ValidateStencilOpEnum(dppass, "dppass"))
   1304    return;
   1305 
   1306  gl->fStencilOpSeparate(face, sfail, dpfail, dppass);
   1307 }
   1308 
   1309 ////////////////////////////////////////////////////////////////////////////////
   1310 // Uniform setters.
   1311 
   1312 void WebGLContext::UniformData(
   1313    const uint32_t loc, const bool transpose,
   1314    const Span<const webgl::UniformDataVal>& data) const {
   1315  const FuncScope funcScope(*this, "uniform setter");
   1316 
   1317  if (!IsWebGL2() && transpose) {
   1318    GenerateError(LOCAL_GL_INVALID_VALUE, "`transpose`:true requires WebGL 2.");
   1319    return;
   1320  }
   1321 
   1322  // -
   1323 
   1324  const auto& link = mActiveProgramLinkInfo;
   1325  if (!link) {
   1326    GenerateError(LOCAL_GL_INVALID_OPERATION, "Active program is not linked.");
   1327    return;
   1328  }
   1329 
   1330  const auto locInfo = MaybeFind(link->locationMap, loc);
   1331  if (!locInfo) {
   1332    // Null WebGLUniformLocations become -1, which will end up here.
   1333    return;
   1334  }
   1335 
   1336  const auto& validationInfo = locInfo->info;
   1337  const auto& activeInfo = validationInfo.info;
   1338  const auto& channels = validationInfo.channelsPerElem;
   1339  const auto& pfn = validationInfo.pfn;
   1340 
   1341  // -
   1342 
   1343  const auto lengthInType = data.size();
   1344  const auto elemCount = lengthInType / channels;
   1345  if (elemCount > 1 && !validationInfo.isArray) {
   1346    GenerateError(
   1347        LOCAL_GL_INVALID_OPERATION,
   1348        "(uniform %s) `values` length (%u) must exactly match size of %s.",
   1349        activeInfo.name.c_str(), (uint32_t)lengthInType,
   1350        EnumString(activeInfo.elemType).c_str());
   1351    return;
   1352  }
   1353 
   1354  // -
   1355 
   1356  const auto& samplerInfo = locInfo->samplerInfo;
   1357  if (samplerInfo) {
   1358    const auto idata = ReinterpretToSpan<const uint32_t>::From(data);
   1359    const auto maxTexUnits = GLMaxTextureUnits();
   1360    for (const auto& val : idata) {
   1361      if (val >= maxTexUnits) {
   1362        ErrorInvalidValue(
   1363            "This uniform location is a sampler, but %d"
   1364            " is not a valid texture unit.",
   1365            val);
   1366        return;
   1367      }
   1368    }
   1369  }
   1370 
   1371  // -
   1372 
   1373  // This is a little galaxy-brain, sorry!
   1374  const auto ptr = static_cast<const void*>(data.data());
   1375  (*pfn)(*gl, static_cast<GLint>(loc), elemCount, transpose, ptr);
   1376 
   1377  // -
   1378 
   1379  if (samplerInfo) {
   1380    auto& texUnits = samplerInfo->texUnits;
   1381 
   1382    const auto srcBegin = reinterpret_cast<const uint32_t*>(data.data());
   1383    auto destIndex = locInfo->indexIntoUniform;
   1384    if (destIndex < texUnits.length()) {
   1385      // Only sample as many indexes as available tex units allow.
   1386      const auto destCount = std::min(elemCount, texUnits.length() - destIndex);
   1387      for (const auto& val : Span<const uint32_t>(srcBegin, destCount)) {
   1388        texUnits[destIndex] = AssertedCast<uint8_t>(val);
   1389        destIndex += 1;
   1390      }
   1391    }
   1392  }
   1393 }
   1394 
   1395 ////////////////////////////////////////////////////////////////////////////////
   1396 
   1397 void WebGLContext::UseProgram(WebGLProgram* prog) {
   1398  FuncScope funcScope(*this, "useProgram");
   1399  if (IsContextLost()) return;
   1400  funcScope.mBindFailureGuard = true;
   1401 
   1402  if (!prog) {
   1403    mCurrentProgram = nullptr;
   1404    mActiveProgramLinkInfo = nullptr;
   1405    funcScope.mBindFailureGuard = false;
   1406    return;
   1407  }
   1408 
   1409  if (!ValidateObject("prog", *prog)) return;
   1410 
   1411  if (!prog->UseProgram()) return;
   1412 
   1413  mCurrentProgram = prog;
   1414  mActiveProgramLinkInfo = mCurrentProgram->LinkInfo();
   1415 
   1416  funcScope.mBindFailureGuard = false;
   1417 }
   1418 
   1419 bool WebGLContext::ValidateProgram(const WebGLProgram& prog) const {
   1420  const FuncScope funcScope(*this, "validateProgram");
   1421  if (IsContextLost()) return false;
   1422 
   1423  return prog.ValidateProgram();
   1424 }
   1425 
   1426 RefPtr<WebGLFramebuffer> WebGLContext::CreateFramebuffer() {
   1427  const FuncScope funcScope(*this, "createFramebuffer");
   1428  if (IsContextLost()) return nullptr;
   1429 
   1430  GLuint fbo = 0;
   1431  gl->fGenFramebuffers(1, &fbo);
   1432 
   1433  return new WebGLFramebuffer(this, fbo);
   1434 }
   1435 
   1436 RefPtr<WebGLFramebuffer> WebGLContext::CreateOpaqueFramebuffer(
   1437    const webgl::OpaqueFramebufferOptions& options) {
   1438  const FuncScope funcScope(*this, "createOpaqueFramebuffer");
   1439  if (IsContextLost()) return nullptr;
   1440 
   1441  uint32_t samples = options.antialias ? StaticPrefs::webgl_msaa_samples() : 0;
   1442  samples = std::min(samples, gl->MaxSamples());
   1443  const gfx::IntSize size = {options.width, options.height};
   1444 
   1445  auto fbo =
   1446      gl::MozFramebuffer::Create(gl, size, samples, options.depthStencil);
   1447  if (!fbo) {
   1448    return nullptr;
   1449  }
   1450 
   1451  return new WebGLFramebuffer(this, std::move(fbo));
   1452 }
   1453 
   1454 RefPtr<WebGLRenderbuffer> WebGLContext::CreateRenderbuffer() {
   1455  const FuncScope funcScope(*this, "createRenderbuffer");
   1456  if (IsContextLost()) return nullptr;
   1457 
   1458  return new WebGLRenderbuffer(this);
   1459 }
   1460 
   1461 void WebGLContext::Viewport(GLint x, GLint y, GLsizei width, GLsizei height) {
   1462  const FuncScope funcScope(*this, "viewport");
   1463  if (IsContextLost()) return;
   1464 
   1465  if (!ValidateNonNegative("width", width) ||
   1466      !ValidateNonNegative("height", height)) {
   1467    return;
   1468  }
   1469 
   1470  const auto& limits = Limits();
   1471  width = std::min(width, static_cast<GLsizei>(limits.maxViewportDim));
   1472  height = std::min(height, static_cast<GLsizei>(limits.maxViewportDim));
   1473 
   1474  gl->fViewport(x, y, width, height);
   1475 
   1476  mViewportX = x;
   1477  mViewportY = y;
   1478  mViewportWidth = width;
   1479  mViewportHeight = height;
   1480 }
   1481 
   1482 void WebGLContext::CompileShader(WebGLShader& shader) {
   1483  const FuncScope funcScope(*this, "compileShader");
   1484  if (IsContextLost()) return;
   1485 
   1486  if (!ValidateObject("shader", shader)) return;
   1487 
   1488  shader.CompileShader();
   1489 }
   1490 
   1491 void WebGLContext::ShaderSource(WebGLShader& shader,
   1492                                const std::string& source) const {
   1493  const FuncScope funcScope(*this, "shaderSource");
   1494  if (IsContextLost()) return;
   1495 
   1496  shader.ShaderSource(source);
   1497 }
   1498 
   1499 void WebGLContext::BlendColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) {
   1500  const FuncScope funcScope(*this, "blendColor");
   1501  if (IsContextLost()) return;
   1502 
   1503  gl->fBlendColor(r, g, b, a);
   1504 }
   1505 
   1506 void WebGLContext::Flush() {
   1507  const FuncScope funcScope(*this, "flush");
   1508  if (IsContextLost()) return;
   1509 
   1510  gl->fFlush();
   1511 }
   1512 
   1513 void WebGLContext::Finish() {
   1514  const FuncScope funcScope(*this, "finish");
   1515  if (IsContextLost()) return;
   1516 
   1517  gl->fFinish();
   1518 
   1519  mCompletedFenceId = mNextFenceId;
   1520  mNextFenceId += 1;
   1521 }
   1522 
   1523 void WebGLContext::LineWidth(GLfloat width) {
   1524  const FuncScope funcScope(*this, "lineWidth");
   1525  if (IsContextLost()) return;
   1526 
   1527  // Doing it this way instead of `if (width <= 0.0)` handles NaNs.
   1528  const bool isValid = width > 0.0;
   1529  if (!isValid) {
   1530    ErrorInvalidValue("`width` must be positive and non-zero.");
   1531    return;
   1532  }
   1533 
   1534  mLineWidth = width;
   1535 
   1536  if (gl->IsCoreProfile() && width > 1.0) {
   1537    width = 1.0;
   1538  }
   1539 
   1540  gl->fLineWidth(width);
   1541 }
   1542 
   1543 void WebGLContext::PolygonOffset(GLfloat factor, GLfloat units) {
   1544  const FuncScope funcScope(*this, "polygonOffset");
   1545  if (IsContextLost()) return;
   1546 
   1547  gl->fPolygonOffset(factor, units);
   1548 }
   1549 
   1550 void WebGLContext::ProvokingVertex(const webgl::ProvokingVertex mode) const {
   1551  const FuncScope funcScope(*this, "provokingVertex");
   1552  if (IsContextLost()) return;
   1553  MOZ_RELEASE_ASSERT(
   1554      IsExtensionEnabled(WebGLExtensionID::WEBGL_provoking_vertex));
   1555 
   1556  gl->fProvokingVertex(UnderlyingValue(mode));
   1557 }
   1558 
   1559 void WebGLContext::SampleCoverage(GLclampf value, WebGLboolean invert) {
   1560  const FuncScope funcScope(*this, "sampleCoverage");
   1561  if (IsContextLost()) return;
   1562 
   1563  gl->fSampleCoverage(value, invert);
   1564 }
   1565 
   1566 }  // namespace mozilla