tor-browser

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

WebGLContext.cpp (94244B)


      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 "WebGLContext.h"
      7 
      8 #include <algorithm>
      9 #include <array>
     10 #include <bitset>
     11 #include <cctype>
     12 #include <queue>
     13 
     14 #include "AccessCheck.h"
     15 #include "CompositableHost.h"
     16 #include "GLBlitHelper.h"
     17 #include "GLContext.h"
     18 #include "GLContextProvider.h"
     19 #include "GLReadTexImageHelper.h"
     20 #include "GLScreenBuffer.h"
     21 #include "ImageContainer.h"
     22 #include "ImageEncoder.h"
     23 #include "LayerUserData.h"
     24 #include "MozFramebuffer.h"
     25 #include "ScopedGLHelpers.h"
     26 #include "SharedSurfaceGL.h"
     27 #include "VRManagerChild.h"
     28 #include "gfxConfig.h"
     29 #include "gfxContext.h"
     30 #include "gfxCrashReporterUtils.h"
     31 #include "gfxEnv.h"
     32 #include "gfxPattern.h"
     33 #include "mozilla/EnumeratedArrayCycleCollection.h"
     34 #include "mozilla/Preferences.h"
     35 #include "mozilla/ProcessPriorityManager.h"
     36 #include "mozilla/ResultVariant.h"
     37 #include "mozilla/SVGObserverUtils.h"
     38 #include "mozilla/ScopeExit.h"
     39 #include "mozilla/Services.h"
     40 #include "mozilla/StaticPrefs_webgl.h"
     41 #include "mozilla/dom/BindingUtils.h"
     42 #include "mozilla/dom/ContentChild.h"
     43 #include "mozilla/dom/Document.h"
     44 #include "mozilla/dom/Event.h"
     45 #include "mozilla/dom/HTMLVideoElement.h"
     46 #include "mozilla/dom/ImageData.h"
     47 #include "mozilla/dom/WebGLContextEvent.h"
     48 #include "mozilla/gfx/Swizzle.h"
     49 #include "mozilla/gfx/gfxVars.h"
     50 #include "mozilla/glean/DomCanvasMetrics.h"
     51 #include "mozilla/layers/BufferTexture.h"
     52 #include "mozilla/layers/CompositorBridgeChild.h"
     53 #include "mozilla/layers/ImageBridgeChild.h"
     54 #include "mozilla/layers/RemoteTextureMap.h"
     55 #include "mozilla/layers/WebRenderCanvasRenderer.h"
     56 #include "mozilla/layers/WebRenderUserData.h"
     57 #include "nsContentUtils.h"
     58 #include "nsDisplayList.h"
     59 #include "nsError.h"
     60 #include "nsIClassInfoImpl.h"
     61 #include "nsIWidget.h"
     62 #include "nsServiceManagerUtils.h"
     63 #include "prenv.h"
     64 
     65 // Local
     66 #include "CanvasUtils.h"
     67 #include "ClientWebGLContext.h"
     68 #include "HostWebGLContext.h"
     69 #include "WebGLBuffer.h"
     70 #include "WebGLChild.h"
     71 #include "WebGLContextLossHandler.h"
     72 #include "WebGLContextUtils.h"
     73 #include "WebGLExtensions.h"
     74 #include "WebGLFormats.h"
     75 #include "WebGLFramebuffer.h"
     76 #include "WebGLMemoryTracker.h"
     77 #include "WebGLObjectModel.h"
     78 #include "WebGLParent.h"
     79 #include "WebGLProgram.h"
     80 #include "WebGLQuery.h"
     81 #include "WebGLSampler.h"
     82 #include "WebGLShader.h"
     83 #include "WebGLShaderValidator.h"
     84 #include "WebGLSync.h"
     85 #include "WebGLTransformFeedback.h"
     86 #include "WebGLValidateStrings.h"
     87 #include "WebGLVertexArray.h"
     88 
     89 #ifdef XP_WIN
     90 #  include "WGLLibrary.h"
     91 #endif
     92 
     93 // Generated
     94 #include "mozilla/dom/WebGLRenderingContextBinding.h"
     95 
     96 namespace mozilla {
     97 
     98 WebGLContextOptions::WebGLContextOptions() {
     99  // Set default alpha state based on preference.
    100  alpha = !StaticPrefs::webgl_default_no_alpha();
    101  antialias = StaticPrefs::webgl_default_antialias();
    102 }
    103 
    104 StaticMutex WebGLContext::sLruMutex;
    105 MOZ_RUNINIT std::list<WebGLContext*> WebGLContext::sLru;
    106 
    107 WebGLContext::LruPosition::LruPosition() {
    108  StaticMutexAutoLock lock(sLruMutex);
    109  mItr = sLru.end();
    110 }  // NOLINT
    111 
    112 WebGLContext::LruPosition::LruPosition(WebGLContext& context) {
    113  StaticMutexAutoLock lock(sLruMutex);
    114  mItr = sLru.insert(sLru.end(), &context);
    115 }
    116 
    117 void WebGLContext::LruPosition::AssignLocked(WebGLContext& aContext) {
    118  ResetLocked();
    119  mItr = sLru.insert(sLru.end(), &aContext);
    120 }
    121 
    122 void WebGLContext::LruPosition::ResetLocked() {
    123  const auto end = sLru.end();
    124  if (mItr != end) {
    125    sLru.erase(mItr);
    126    mItr = end;
    127  }
    128 }
    129 
    130 void WebGLContext::LruPosition::Reset() {
    131  StaticMutexAutoLock lock(sLruMutex);
    132  ResetLocked();
    133 }
    134 
    135 bool WebGLContext::LruPosition::IsInsertedLocked() const {
    136  return mItr != sLru.end();
    137 }
    138 
    139 WebGLContext::WebGLContext(HostWebGLContext* host,
    140                           const webgl::InitContextDesc& desc)
    141    : gl(mGL_OnlyClearInDestroyResourcesAndContext),  // const reference
    142      mHost(host),
    143      mResistFingerprinting(desc.resistFingerprinting),
    144      mOptions(desc.options),
    145      mPrincipalKey(desc.principalKey),
    146      mContextLossHandler(this),
    147      mRequestedSize(desc.size) {
    148  if (host) {
    149    host->mContext = this;
    150  }
    151  const FuncScope funcScope(*this, "<Create>");
    152  WebGLMemoryTracker::EnsureRegistered();
    153 }
    154 
    155 WebGLContext::~WebGLContext() { DestroyResourcesAndContext(); }
    156 
    157 void WebGLContext::DestroyResourcesAndContext() {
    158  if (mRemoteTextureOwner) {
    159    // Clean up any remote textures registered for framebuffer swap chains.
    160    mRemoteTextureOwner->UnregisterAllTextureOwners();
    161    mRemoteTextureOwner = nullptr;
    162  }
    163 
    164  if (!gl) return;
    165 
    166  mDefaultFB = nullptr;
    167  mResolvedDefaultFB = nullptr;
    168 
    169  mBound2DTextures.Clear();
    170  mBoundCubeMapTextures.Clear();
    171  mBound3DTextures.Clear();
    172  mBound2DArrayTextures.Clear();
    173  mBoundSamplers.Clear();
    174  mBoundArrayBuffer = nullptr;
    175  mBoundCopyReadBuffer = nullptr;
    176  mBoundCopyWriteBuffer = nullptr;
    177  mBoundPixelPackBuffer = nullptr;
    178  mBoundPixelUnpackBuffer = nullptr;
    179  mBoundTransformFeedbackBuffer = nullptr;
    180  mBoundUniformBuffer = nullptr;
    181  mCurrentProgram = nullptr;
    182  mActiveProgramLinkInfo = nullptr;
    183  mBoundDrawFramebuffer = nullptr;
    184  mBoundReadFramebuffer = nullptr;
    185  mBoundVertexArray = nullptr;
    186  mDefaultVertexArray = nullptr;
    187  mBoundTransformFeedback = nullptr;
    188  mDefaultTransformFeedback = nullptr;
    189 
    190  mQuerySlot_SamplesPassed = nullptr;
    191  mQuerySlot_TFPrimsWritten = nullptr;
    192  mQuerySlot_TimeElapsed = nullptr;
    193 
    194  mIndexedUniformBufferBindings.clear();
    195 
    196  //////
    197 
    198  if (mEmptyTFO) {
    199    gl->fDeleteTransformFeedbacks(1, &mEmptyTFO);
    200    mEmptyTFO = 0;
    201  }
    202 
    203  //////
    204 
    205  if (mFakeVertexAttrib0BufferObject) {
    206    gl->fDeleteBuffers(1, &mFakeVertexAttrib0BufferObject);
    207    mFakeVertexAttrib0BufferObject = 0;
    208  }
    209 
    210  // disable all extensions except "WEBGL_lose_context". see bug #927969
    211  // spec: http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
    212  for (size_t i = 0; i < size_t(WebGLExtensionID::Max); ++i) {
    213    WebGLExtensionID extension = WebGLExtensionID(i);
    214    if (extension == WebGLExtensionID::WEBGL_lose_context) continue;
    215    mExtensions[extension] = nullptr;
    216  }
    217 
    218  // We just got rid of everything, so the context had better
    219  // have been going away.
    220  if (gl::GLContext::ShouldSpew()) {
    221    printf_stderr("--- WebGL context destroyed: %p\n", gl.get());
    222  }
    223 
    224  MOZ_ASSERT(gl);
    225  gl->MarkDestroyed();
    226  mGL_OnlyClearInDestroyResourcesAndContext = nullptr;
    227  MOZ_ASSERT(!gl);
    228 }
    229 
    230 void ClientWebGLContext::MarkCanvasDirty() {
    231  if (!mCanvasElement && !mOffscreenCanvas) return;
    232 
    233  mFrameCaptureState = FrameCaptureState::DIRTY;
    234 
    235  if (mIsCanvasDirty) return;
    236  mIsCanvasDirty = true;
    237 
    238  if (mCanvasElement) {
    239    SVGObserverUtils::InvalidateDirectRenderingObservers(mCanvasElement);
    240    mCanvasElement->InvalidateCanvasContent(nullptr);
    241  } else if (mOffscreenCanvas) {
    242    mOffscreenCanvas->QueueCommitToCompositor();
    243  }
    244 }
    245 
    246 void WebGLContext::OnMemoryPressure() {
    247  bool shouldLoseContext = mLoseContextOnMemoryPressure;
    248 
    249  if (!mCanLoseContextInForeground &&
    250      ProcessPriorityManager::CurrentProcessIsForeground()) {
    251    shouldLoseContext = false;
    252  }
    253 
    254  if (shouldLoseContext) LoseContext();
    255 }
    256 
    257 // --
    258 
    259 bool WebGLContext::CreateAndInitGL(
    260    bool forceEnabled, std::vector<FailureReason>* const out_failReasons) {
    261  const FuncScope funcScope(*this, "<Create>");
    262 
    263  // WebGL2 is separately blocked:
    264  if (IsWebGL2() && !forceEnabled) {
    265    FailureReason reason;
    266    if (!gfx::gfxVars::AllowWebgl2()) {
    267      reason.info =
    268          "AllowWebgl2:false restricts context creation on this system.";
    269      out_failReasons->push_back(reason);
    270      GenerateWarning("%s", reason.info.BeginReading());
    271      return false;
    272    }
    273  }
    274 
    275  auto flags = gl::CreateContextFlags::PREFER_ROBUSTNESS;
    276 
    277  if (StaticPrefs::webgl_gl_khr_no_error()) {
    278    flags |= gl::CreateContextFlags::NO_VALIDATION;
    279  }
    280 
    281  // -
    282 
    283  if (StaticPrefs::webgl_forbid_hardware()) {
    284    flags |= gl::CreateContextFlags::FORBID_HARDWARE;
    285  }
    286  if (StaticPrefs::webgl_forbid_software()) {
    287    flags |= gl::CreateContextFlags::FORBID_SOFTWARE;
    288  }
    289 
    290  if (mOptions.forceSoftwareRendering) {
    291    flags |= gl::CreateContextFlags::FORBID_HARDWARE;
    292    flags &= ~gl::CreateContextFlags::FORBID_SOFTWARE;
    293  }
    294 
    295  if (forceEnabled) {
    296    flags &= ~gl::CreateContextFlags::FORBID_HARDWARE;
    297    flags &= ~gl::CreateContextFlags::FORBID_SOFTWARE;
    298  }
    299 
    300  if ((flags & gl::CreateContextFlags::FORBID_HARDWARE) &&
    301      (flags & gl::CreateContextFlags::FORBID_SOFTWARE)) {
    302    FailureReason reason;
    303    reason.info = "Both hardware and software were forbidden by config.";
    304    out_failReasons->push_back(reason);
    305    GenerateWarning("%s", reason.info.BeginReading());
    306    return false;
    307  }
    308 
    309  // -
    310 
    311  if (StaticPrefs::webgl_cgl_multithreaded()) {
    312    flags |= gl::CreateContextFlags::PREFER_MULTITHREADED;
    313  }
    314 
    315  if (IsWebGL2()) {
    316    flags |= gl::CreateContextFlags::PREFER_ES3;
    317  } else {
    318    if (StaticPrefs::webgl_1_request_es2()) {
    319      // Request and prefer ES2 context for WebGL1.
    320      flags |= gl::CreateContextFlags::PREFER_EXACT_VERSION;
    321    }
    322 
    323    if (!StaticPrefs::webgl_1_allow_core_profiles()) {
    324      flags |= gl::CreateContextFlags::REQUIRE_COMPAT_PROFILE;
    325    }
    326  }
    327 
    328  {
    329    auto powerPref = mOptions.powerPreference;
    330 
    331    // If "Use hardware acceleration when available" option is disabled:
    332    if (!gfx::gfxConfig::IsEnabled(gfx::Feature::HW_COMPOSITING)) {
    333      powerPref = dom::WebGLPowerPreference::Low_power;
    334    }
    335 
    336    const auto overrideVal = StaticPrefs::webgl_power_preference_override();
    337    if (overrideVal > 0) {
    338      powerPref = dom::WebGLPowerPreference::High_performance;
    339    } else if (overrideVal < 0) {
    340      powerPref = dom::WebGLPowerPreference::Low_power;
    341    }
    342 
    343    if (powerPref == dom::WebGLPowerPreference::High_performance) {
    344      flags |= gl::CreateContextFlags::HIGH_POWER;
    345    }
    346  }
    347 
    348  if (!gfx::gfxVars::WebglAllowCoreProfile()) {
    349    flags |= gl::CreateContextFlags::REQUIRE_COMPAT_PROFILE;
    350  }
    351 
    352  // --
    353 
    354  const bool useEGL = PR_GetEnv("MOZ_WEBGL_FORCE_EGL");
    355 
    356  bool tryNativeGL = true;
    357  bool tryANGLE = false;
    358 
    359 #ifdef XP_WIN
    360  tryNativeGL = false;
    361  tryANGLE = true;
    362 
    363  if (StaticPrefs::webgl_disable_wgl()) {
    364    tryNativeGL = false;
    365  }
    366 
    367  if (StaticPrefs::webgl_disable_angle() ||
    368      PR_GetEnv("MOZ_WEBGL_FORCE_OPENGL") || useEGL) {
    369    tryNativeGL = true;
    370    tryANGLE = false;
    371  }
    372 #endif
    373 
    374  if (tryNativeGL && !forceEnabled) {
    375    FailureReason reason;
    376    if (!gfx::gfxVars::WebglAllowWindowsNativeGl()) {
    377      reason.info =
    378          "WebglAllowWindowsNativeGl:false restricts context creation on this "
    379          "system.";
    380 
    381      out_failReasons->push_back(reason);
    382 
    383      GenerateWarning("%s", reason.info.BeginReading());
    384      tryNativeGL = false;
    385    }
    386  }
    387 
    388  // --
    389 
    390  using fnCreateT = decltype(gl::GLContextProviderEGL::CreateHeadless);
    391  const auto fnCreate = [&](fnCreateT* const pfnCreate,
    392                            const char* const info) {
    393    nsCString failureId;
    394    const RefPtr<gl::GLContext> gl = pfnCreate({flags}, &failureId);
    395    if (!gl) {
    396      out_failReasons->push_back(WebGLContext::FailureReason(failureId, info));
    397    }
    398    return gl;
    399  };
    400 
    401  const auto newGL = [&]() -> RefPtr<gl::GLContext> {
    402    if (tryNativeGL) {
    403      if (useEGL)
    404        return fnCreate(&gl::GLContextProviderEGL::CreateHeadless, "useEGL");
    405 
    406      const auto ret =
    407          fnCreate(&gl::GLContextProvider::CreateHeadless, "tryNativeGL");
    408      if (ret) return ret;
    409    }
    410 
    411    if (tryANGLE) {
    412      return fnCreate(&gl::GLContextProviderEGL::CreateHeadless, "tryANGLE");
    413    }
    414    return nullptr;
    415  }();
    416 
    417  if (!newGL) {
    418    out_failReasons->push_back(
    419        FailureReason("FEATURE_FAILURE_WEBGL_EXHAUSTED_DRIVERS",
    420                      "Exhausted GL driver options."));
    421    return false;
    422  }
    423 
    424  // --
    425 
    426  FailureReason reason;
    427 
    428  mGL_OnlyClearInDestroyResourcesAndContext = newGL;
    429  MOZ_RELEASE_ASSERT(gl);
    430  if (!InitAndValidateGL(&reason)) {
    431    DestroyResourcesAndContext();
    432    MOZ_RELEASE_ASSERT(!gl);
    433 
    434    // The fail reason here should be specific enough for now.
    435    out_failReasons->push_back(reason);
    436    return false;
    437  }
    438 
    439  const auto val = StaticPrefs::webgl_debug_incomplete_tex_color();
    440  if (val) {
    441    mIncompleteTexOverride.reset(new gl::Texture(*gl));
    442    const gl::ScopedBindTexture autoBind(gl, mIncompleteTexOverride->name);
    443    const auto heapVal = std::make_unique<uint32_t>(val);
    444    gl->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGBA, 1, 1, 0,
    445                    LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, heapVal.get());
    446  }
    447 
    448  return true;
    449 }
    450 
    451 // Fallback for resizes:
    452 
    453 bool WebGLContext::EnsureDefaultFB() {
    454  if (mDefaultFB) {
    455    MOZ_ASSERT(*uvec2::FromSize(mDefaultFB->mSize) == mRequestedSize);
    456    return true;
    457  }
    458 
    459  const bool depthStencil = mOptions.depth || mOptions.stencil;
    460  auto attemptSize = gfx::IntSize{mRequestedSize.x, mRequestedSize.y};
    461 
    462  while (attemptSize.width || attemptSize.height) {
    463    attemptSize.width = std::max(attemptSize.width, 1);
    464    attemptSize.height = std::max(attemptSize.height, 1);
    465 
    466    [&]() {
    467      if (mOptions.antialias) {
    468        MOZ_ASSERT(!mDefaultFB);
    469        mDefaultFB = gl::MozFramebuffer::Create(gl, attemptSize, mMsaaSamples,
    470                                                depthStencil);
    471        if (mDefaultFB) return;
    472        if (mOptionsFrozen) return;
    473      }
    474 
    475      MOZ_ASSERT(!mDefaultFB);
    476      mDefaultFB = gl::MozFramebuffer::Create(gl, attemptSize, 0, depthStencil);
    477    }();
    478 
    479    if (mDefaultFB) break;
    480 
    481    attemptSize.width /= 2;
    482    attemptSize.height /= 2;
    483  }
    484 
    485  if (!mDefaultFB) {
    486    GenerateWarning("Backbuffer resize failed. Losing context.");
    487    LoseContext();
    488    return false;
    489  }
    490 
    491  mDefaultFB_IsInvalid = true;
    492 
    493  const auto actualSize = *uvec2::FromSize(mDefaultFB->mSize);
    494  if (actualSize != mRequestedSize) {
    495    GenerateWarning(
    496        "Requested size %ux%u was too large, but resize"
    497        " to %ux%u succeeded.",
    498        mRequestedSize.x, mRequestedSize.y, actualSize.x, actualSize.y);
    499  }
    500  mRequestedSize = actualSize;
    501  return true;
    502 }
    503 
    504 void WebGLContext::Resize(uvec2 requestedSize) {
    505  // Zero-sized surfaces can cause problems.
    506  if (!requestedSize.x) {
    507    requestedSize.x = 1;
    508  }
    509  if (!requestedSize.y) {
    510    requestedSize.y = 1;
    511  }
    512 
    513  // Kill our current default fb(s), for later lazy allocation.
    514  mRequestedSize = requestedSize;
    515  mDefaultFB = nullptr;
    516  mResolvedDefaultFB = nullptr;
    517  mResetLayer = true;  // New size means new Layer.
    518 }
    519 
    520 std::unique_ptr<webgl::FormatUsageAuthority> WebGLContext::CreateFormatUsage(
    521    gl::GLContext* gl) const {
    522  return webgl::FormatUsageAuthority::CreateForWebGL1(gl);
    523 }
    524 
    525 /*static*/
    526 RefPtr<WebGLContext> WebGLContext::Create(HostWebGLContext* host,
    527                                          const webgl::InitContextDesc& desc,
    528                                          webgl::InitContextResult* const out) {
    529  AUTO_PROFILER_LABEL("WebGLContext::Create", GRAPHICS);
    530  nsCString failureId = "FEATURE_FAILURE_WEBGL_UNKOWN"_ns;
    531  const bool forceEnabled = StaticPrefs::webgl_force_enabled();
    532  ScopedGfxFeatureReporter reporter("WebGL", forceEnabled);
    533 
    534  auto res = [&]() -> Result<RefPtr<WebGLContext>, std::string> {
    535    bool disabled = StaticPrefs::webgl_disabled();
    536 
    537    // TODO: When we have software webgl support we should use that instead.
    538    disabled |= gfxPlatform::InSafeMode();
    539 
    540    if (disabled) {
    541      if (gfxPlatform::InSafeMode()) {
    542        failureId = "FEATURE_FAILURE_WEBGL_SAFEMODE"_ns;
    543      } else {
    544        failureId = "FEATURE_FAILURE_WEBGL_DISABLED"_ns;
    545      }
    546      return Err("WebGL is currently disabled.");
    547    }
    548 
    549    // Alright, now let's start trying.
    550 
    551    RefPtr<WebGLContext> webgl;
    552    if (desc.isWebgl2) {
    553      webgl = new WebGL2Context(host, desc);
    554    } else {
    555      webgl = new WebGLContext(host, desc);
    556    }
    557 
    558    MOZ_ASSERT(!webgl->gl);
    559    std::vector<FailureReason> failReasons;
    560    if (!webgl->CreateAndInitGL(forceEnabled, &failReasons)) {
    561      nsCString text("WebGL creation failed: ");
    562      for (const auto& cur : failReasons) {
    563        // Don't try to accumulate using an empty key if |cur.key| is empty.
    564        if (cur.key.IsEmpty()) {
    565          glean::canvas::webgl_failure_id
    566              .Get("FEATURE_FAILURE_REASON_UNKNOWN"_ns)
    567              .Add(1);
    568        } else {
    569          glean::canvas::webgl_failure_id.Get(cur.key).Add(1);
    570        }
    571 
    572        const auto str = nsPrintfCString("\n* %s (%s)", cur.info.BeginReading(),
    573                                         cur.key.BeginReading());
    574        text.Append(str);
    575      }
    576      failureId = "FEATURE_FAILURE_REASON"_ns;
    577      return Err(text.BeginReading());
    578    }
    579    MOZ_ASSERT(webgl->gl);
    580 
    581    if (desc.options.failIfMajorPerformanceCaveat) {
    582      if (webgl->gl->IsWARP()) {
    583        failureId = "FEATURE_FAILURE_WEBGL_PERF_WARP"_ns;
    584        return Err(
    585            "failIfMajorPerformanceCaveat: Driver is not"
    586            " hardware-accelerated.");
    587      }
    588 
    589 #ifdef XP_WIN
    590      if (webgl->gl->GetContextType() == gl::GLContextType::WGL &&
    591          !gl::sWGLLib.HasDXInterop2()) {
    592        failureId = "FEATURE_FAILURE_WEBGL_DXGL_INTEROP2"_ns;
    593        return Err("failIfMajorPerformanceCaveat: WGL without DXGLInterop2.");
    594      }
    595 #endif
    596    }
    597 
    598    const FuncScope funcScope(*webgl, "getContext/restoreContext");
    599 
    600    MOZ_ASSERT(!webgl->mDefaultFB);
    601    if (!webgl->EnsureDefaultFB()) {
    602      MOZ_ASSERT(!webgl->mDefaultFB);
    603      MOZ_ASSERT(webgl->IsContextLost());
    604      failureId = "FEATURE_FAILURE_WEBGL_BACKBUFFER"_ns;
    605      return Err("Initializing WebGL backbuffer failed.");
    606    }
    607 
    608    return webgl;
    609  }();
    610  if (res.isOk()) {
    611    failureId = "SUCCESS"_ns;
    612  }
    613  glean::canvas::webgl_failure_id.Get(failureId).Add(1);
    614 
    615  if (!res.isOk()) {
    616    out->error = res.unwrapErr();
    617    return nullptr;
    618  }
    619  const auto webgl = res.unwrap();
    620 
    621  // Update our internal stuff:
    622  webgl->FinishInit();
    623 
    624  reporter.SetSuccessful();
    625  if (gl::GLContext::ShouldSpew()) {
    626    printf_stderr("--- WebGL context created: %p\n", webgl.get());
    627  }
    628 
    629  // -
    630 
    631  const auto UploadableSdTypes = [&]() {
    632    webgl::EnumMask<layers::SurfaceDescriptor::Type> types;
    633    types[layers::SurfaceDescriptor::TSurfaceDescriptorBuffer] = true;
    634    // Only support canvas surface interchange if using AC2D. This guarantees
    635    // that WebGL and AC2D commands are sequenced and processed on the same
    636    // thread, so that there is no mal-ordering between AC2D and WebGL
    637    // processing. We can flush out AC2D commands to produce a surface in time
    638    // for WebGL to use without requiring any blocking to occur.
    639    types[layers::SurfaceDescriptor::TSurfaceDescriptorCanvasSurface] =
    640        gfx::gfxVars::UseAcceleratedCanvas2D();
    641    // This is conditional on not using the Compositor thread because we may
    642    // need to synchronize with the RDD process over the PVideoBridge protocol
    643    // to wait for the texture to be available in the compositor process. We
    644    // cannot block on the Compositor thread, so in that configuration, we would
    645    // prefer to do the readback from the RDD which is guaranteed to work, and
    646    // only block the owning thread for WebGL.
    647    const bool offCompositorThread = gfx::gfxVars::UseCanvasRenderThread() ||
    648                                     !gfx::gfxVars::SupportsThreadsafeGL();
    649    types[layers::SurfaceDescriptor::TSurfaceDescriptorGPUVideo] =
    650        offCompositorThread;
    651    // Similarly to the PVideoBridge protocol, we may need to synchronize with
    652    // the content process over the PCompositorManager protocol to wait for the
    653    // shared surface to be available in the compositor process, and we cannot
    654    // block on the Compositor thread.
    655    types[layers::SurfaceDescriptor::TSurfaceDescriptorExternalImage] =
    656        offCompositorThread;
    657    if (webgl->gl->IsANGLE()) {
    658      types[layers::SurfaceDescriptor::TSurfaceDescriptorD3D10] = true;
    659      types[layers::SurfaceDescriptor::TSurfaceDescriptorDXGIYCbCr] = true;
    660    }
    661    if (kIsMacOS) {
    662      types[layers::SurfaceDescriptor::TSurfaceDescriptorMacIOSurface] = true;
    663    }
    664    if (kIsAndroid) {
    665      types[layers::SurfaceDescriptor::TSurfaceTextureDescriptor] = true;
    666    }
    667    if (kIsLinux) {
    668      types[layers::SurfaceDescriptor::TSurfaceDescriptorDMABuf] = true;
    669    }
    670    return types;
    671  };
    672 
    673  // -
    674 
    675  constexpr GLenum SHADER_TYPES[] = {
    676      LOCAL_GL_VERTEX_SHADER,
    677      LOCAL_GL_FRAGMENT_SHADER,
    678  };
    679  constexpr GLenum PRECISIONS[] = {
    680      LOCAL_GL_LOW_FLOAT, LOCAL_GL_MEDIUM_FLOAT, LOCAL_GL_HIGH_FLOAT,
    681      LOCAL_GL_LOW_INT,   LOCAL_GL_MEDIUM_INT,   LOCAL_GL_HIGH_INT,
    682  };
    683  for (const auto& shaderType : SHADER_TYPES) {
    684    for (const auto& precisionType : PRECISIONS) {
    685      auto spf = webgl::ShaderPrecisionFormat{};
    686 
    687      GLint range[2] = {};
    688      GLint precision = 0;
    689      webgl->gl->fGetShaderPrecisionFormat(shaderType, precisionType, range,
    690                                           &precision);
    691      spf.rangeMin = LazyAssertedCast(range[0]);
    692      spf.rangeMax = LazyAssertedCast(range[1]);
    693      spf.precision = LazyAssertedCast(precision);
    694 
    695      out->shaderPrecisions->insert({{shaderType, precisionType}, spf});
    696    }
    697  }
    698 
    699  if (webgl->mDisableFragHighP) {
    700    out->shaderPrecisions->at(
    701        {LOCAL_GL_FRAGMENT_SHADER, LOCAL_GL_HIGH_FLOAT}) = {};
    702    out->shaderPrecisions->at(
    703        {LOCAL_GL_FRAGMENT_SHADER, LOCAL_GL_HIGH_INT}) = {};
    704  }
    705 
    706  // -
    707 
    708  out->options = webgl->mOptions;
    709  out->limits = *webgl->mLimits;
    710  out->uploadableSdTypes = UploadableSdTypes();
    711  out->vendor = webgl->gl->Vendor();
    712  out->optionalRenderableFormatBits = webgl->mOptionalRenderableFormatBits;
    713 
    714  return webgl;
    715 }
    716 
    717 void WebGLContext::FinishInit() {
    718  mOptions.antialias &= bool(mDefaultFB->mSamples);
    719 
    720  if (!mOptions.alpha) {
    721    // We always have alpha.
    722    mNeedsFakeNoAlpha = true;
    723  }
    724 
    725  if (mOptions.depth || mOptions.stencil) {
    726    // We always have depth+stencil if we have either.
    727    if (!mOptions.depth) {
    728      mNeedsFakeNoDepth = true;
    729    }
    730    if (!mOptions.stencil) {
    731      mNeedsFakeNoStencil = true;
    732    }
    733  }
    734 
    735  mResetLayer = true;
    736  mOptionsFrozen = true;
    737 
    738  //////
    739  // Initial setup.
    740 
    741  gl->mImplicitMakeCurrent = true;
    742  gl->mElideDuplicateBindFramebuffers = true;
    743 
    744  const auto& size = mDefaultFB->mSize;
    745 
    746  mViewportX = mViewportY = 0;
    747  mViewportWidth = size.width;
    748  mViewportHeight = size.height;
    749  gl->fViewport(mViewportX, mViewportY, mViewportWidth, mViewportHeight);
    750 
    751  mScissorRect = {0, 0, size.width, size.height};
    752  mScissorRect.Apply(*gl);
    753 
    754  {
    755    const auto& isEnabledMap = webgl::MakeIsEnabledMap(IsWebGL2());
    756    for (const auto& pair : isEnabledMap) {
    757      mIsEnabledMapKeys.insert(pair.first);
    758    }
    759  }
    760 
    761  //////
    762  // Check everything
    763 
    764  AssertCachedBindings();
    765  AssertCachedGlobalState();
    766 
    767  mShouldPresent = true;
    768 
    769  //////
    770  // mIsRgb8Renderable
    771 
    772  {
    773    const auto tex = gl::ScopedTexture(gl);
    774    const auto fb = gl::ScopedFramebuffer(gl);
    775    gl->fBindTexture(LOCAL_GL_TEXTURE_2D, tex);
    776    gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fb);
    777    gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
    778                              LOCAL_GL_TEXTURE_2D, tex, 0);
    779 
    780    const auto IsRenderable = [&](const GLint internalFormat,
    781                                  const GLenum unpackFormat) {
    782      gl->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, internalFormat, 1, 1, 0,
    783                      unpackFormat, LOCAL_GL_UNSIGNED_BYTE, nullptr);
    784      const auto status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
    785      return (status == LOCAL_GL_FRAMEBUFFER_COMPLETE);
    786    };
    787 
    788    if (IsRenderable(LOCAL_GL_RGB, LOCAL_GL_RGB)) {
    789      mOptionalRenderableFormatBits |=
    790          webgl::OptionalRenderableFormatBits::RGB8;
    791    }
    792    if (gl->IsSupported(gl::GLFeature::sRGB)) {
    793      struct {
    794        GLint internal;
    795        GLenum unpack;
    796      } formats = {LOCAL_GL_SRGB8, LOCAL_GL_RGB};
    797      const bool isEs2 = (gl->IsGLES() && gl->Version() < 300);
    798      if (isEs2) {
    799        formats = {LOCAL_GL_SRGB, LOCAL_GL_SRGB};
    800      }
    801      if (IsRenderable(formats.internal, formats.unpack)) {
    802        mOptionalRenderableFormatBits |=
    803            webgl::OptionalRenderableFormatBits::SRGB8;
    804      }
    805    }
    806  }
    807 
    808  //////
    809 
    810  gl->ResetSyncCallCount("WebGLContext Initialization");
    811  LoseLruContextIfLimitExceeded();
    812 }
    813 
    814 void WebGLContext::SetCompositableHost(
    815    RefPtr<layers::CompositableHost>& aCompositableHost) {
    816  mCompositableHost = aCompositableHost;
    817 }
    818 
    819 void WebGLContext::BumpLruLocked() {
    820  if (!mIsContextLost && !mPendingContextLoss) {
    821    mLruPosition.AssignLocked(*this);
    822  } else {
    823    MOZ_ASSERT(!mLruPosition.IsInsertedLocked());
    824  }
    825 }
    826 
    827 void WebGLContext::BumpLru() {
    828  StaticMutexAutoLock lock(sLruMutex);
    829  BumpLruLocked();
    830 }
    831 
    832 void WebGLContext::LoseLruContextIfLimitExceeded() {
    833  StaticMutexAutoLock lock(sLruMutex);
    834 
    835  const auto maxContexts = std::max(1u, StaticPrefs::webgl_max_contexts());
    836  const auto maxContextsPerPrincipal =
    837      std::max(1u, StaticPrefs::webgl_max_contexts_per_principal());
    838 
    839  // it's important to update the index on a new context before losing old
    840  // contexts, otherwise new unused contexts would all have index 0 and we
    841  // couldn't distinguish older ones when choosing which one to lose first.
    842  BumpLruLocked();
    843 
    844  {
    845    size_t forPrincipal = 0;
    846    for (const auto& context : sLru) {
    847      if (context->mPrincipalKey == mPrincipalKey) {
    848        forPrincipal += 1;
    849      }
    850    }
    851 
    852    while (forPrincipal > maxContextsPerPrincipal) {
    853      const auto text = nsPrintfCString(
    854          "Exceeded %u live WebGL contexts for this principal, losing the "
    855          "least recently used one.",
    856          maxContextsPerPrincipal);
    857      JsWarning(ToString(text));
    858 
    859      for (const auto& context : sLru) {
    860        if (context->mPrincipalKey == mPrincipalKey) {
    861          MOZ_ASSERT(context != this);
    862          context->LoseContextLruLocked(webgl::ContextLossReason::None);
    863          forPrincipal -= 1;
    864          break;
    865        }
    866      }
    867    }
    868  }
    869 
    870  auto total = sLru.size();
    871  while (total > maxContexts) {
    872    const auto text = nsPrintfCString(
    873        "Exceeded %u live WebGL contexts, losing the least "
    874        "recently used one.",
    875        maxContexts);
    876    JsWarning(ToString(text));
    877 
    878    const auto& context = sLru.front();
    879    MOZ_ASSERT(context != this);
    880    context->LoseContextLruLocked(webgl::ContextLossReason::None);
    881    total -= 1;
    882  }
    883 }
    884 
    885 // -
    886 
    887 namespace webgl {
    888 
    889 ScopedPrepForResourceClear::ScopedPrepForResourceClear(
    890    const WebGLContext& webgl_)
    891    : webgl(webgl_) {
    892  const auto& gl = webgl.gl;
    893 
    894  if (webgl.mScissorTestEnabled) {
    895    gl->fDisable(LOCAL_GL_SCISSOR_TEST);
    896  }
    897  if (webgl.mRasterizerDiscardEnabled) {
    898    gl->fDisable(LOCAL_GL_RASTERIZER_DISCARD);
    899  }
    900 
    901  // "The clear operation always uses the front stencil write mask
    902  //  when clearing the stencil buffer."
    903  webgl.DoColorMask(Some(0), 0b1111);
    904  gl->fDepthMask(true);
    905  gl->fStencilMaskSeparate(LOCAL_GL_FRONT, 0xffffffff);
    906 
    907  gl->fClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    908  gl->fClearDepth(1.0f);  // Depth formats are always cleared to 1.0f, not 0.0f.
    909  gl->fClearStencil(0);
    910 }
    911 
    912 ScopedPrepForResourceClear::~ScopedPrepForResourceClear() {
    913  const auto& gl = webgl.gl;
    914 
    915  if (webgl.mScissorTestEnabled) {
    916    gl->fEnable(LOCAL_GL_SCISSOR_TEST);
    917  }
    918  if (webgl.mRasterizerDiscardEnabled) {
    919    gl->fEnable(LOCAL_GL_RASTERIZER_DISCARD);
    920  }
    921 
    922  webgl.DoColorMask(Some(0), webgl.mColorWriteMask0);
    923  gl->fDepthMask(webgl.mDepthWriteMask);
    924  gl->fStencilMaskSeparate(LOCAL_GL_FRONT, webgl.mStencilWriteMaskFront);
    925 
    926  gl->fClearColor(webgl.mColorClearValue[0], webgl.mColorClearValue[1],
    927                  webgl.mColorClearValue[2], webgl.mColorClearValue[3]);
    928  gl->fClearDepth(webgl.mDepthClearValue);
    929  gl->fClearStencil(webgl.mStencilClearValue);
    930 }
    931 
    932 }  // namespace webgl
    933 
    934 // -
    935 
    936 void WebGLContext::OnEndOfFrame() {
    937  if (StaticPrefs::webgl_perf_spew_frame_allocs()) {
    938    GeneratePerfWarning("[webgl.perf.spew-frame-allocs] %" PRIu64
    939                        " data allocations this frame.",
    940                        mDataAllocGLCallCount);
    941  }
    942  mDataAllocGLCallCount = 0;
    943  gl->ResetSyncCallCount("WebGLContext PresentScreenBuffer");
    944 
    945  mDrawCallsSinceLastFlush = 0;
    946 
    947  PollPendingSyncs();
    948 
    949  BumpLru();
    950 }
    951 
    952 void WebGLContext::BlitBackbufferToCurDriverFB(
    953    WebGLFramebuffer* const srcAsWebglFb,
    954    const gl::MozFramebuffer* const srcAsMozFb, bool srcIsBGRA, bool yFlip,
    955    Maybe<gfxAlphaType> convertAlpha) const {
    956  // BlitFramebuffer ignores ColorMask().
    957 
    958  if (mScissorTestEnabled) {
    959    gl->fDisable(LOCAL_GL_SCISSOR_TEST);
    960  }
    961  const auto cleanup = MakeScopeExit([&]() {
    962    if (mScissorTestEnabled) {
    963      gl->fEnable(LOCAL_GL_SCISSOR_TEST);
    964    }
    965  });
    966 
    967  // If a MozFramebuffer is supplied, ensure that a WebGLFramebuffer is not
    968  // used since it might not have completeness info, while the MozFramebuffer
    969  // can still supply the needed information.
    970  MOZ_ASSERT(!(srcAsMozFb && srcAsWebglFb));
    971  const auto* mozFb = srcAsMozFb ? srcAsMozFb : mDefaultFB.get();
    972  GLuint fbo = 0;
    973  gfx::IntSize size;
    974  if (srcAsWebglFb) {
    975    fbo = srcAsWebglFb->mGLName;
    976    const auto* info = srcAsWebglFb->GetCompletenessInfo();
    977    MOZ_ASSERT(info);
    978    size = gfx::IntSize(info->width, info->height);
    979  } else {
    980    fbo = mozFb->mFB;
    981    size = mozFb->mSize;
    982  }
    983 
    984  // If no format conversion is necessary, then attempt to directly blit
    985  // between framebuffers. Otherwise, if we need to convert to RGBA from
    986  // the source format or do other conversions, then we will need to use
    987  // the texture blit path below.
    988  if (!srcIsBGRA && !yFlip && !convertAlpha) {
    989    if (gl->IsSupported(gl::GLFeature::framebuffer_blit)) {
    990      gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, fbo);
    991      gl->fBlitFramebuffer(0, 0, size.width, size.height, 0, 0, size.width,
    992                           size.height, LOCAL_GL_COLOR_BUFFER_BIT,
    993                           LOCAL_GL_NEAREST);
    994      return;
    995    }
    996    if (mDefaultFB->mSamples &&
    997        gl->IsExtensionSupported(
    998            gl::GLContext::APPLE_framebuffer_multisample)) {
    999      gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, fbo);
   1000      gl->fResolveMultisampleFramebufferAPPLE();
   1001      return;
   1002    }
   1003  }
   1004 
   1005  GLuint colorTex = 0;
   1006  if (srcAsWebglFb) {
   1007    const auto& attach = srcAsWebglFb->ColorAttachment0();
   1008    MOZ_ASSERT(attach.Texture());
   1009    colorTex = attach.Texture()->mGLName;
   1010  } else {
   1011    colorTex = mozFb->ColorTex();
   1012  }
   1013 
   1014  // DrawBlit handles ColorMask itself.
   1015  gl->BlitHelper()->DrawBlitTextureToFramebuffer(colorTex, size, size,
   1016                                                 LOCAL_GL_TEXTURE_2D, srcIsBGRA,
   1017                                                 yFlip, convertAlpha);
   1018 }
   1019 
   1020 // -
   1021 
   1022 template <typename T, typename... Args>
   1023 constexpr auto MakeArray(Args... args) -> std::array<T, sizeof...(Args)> {
   1024  return {{static_cast<T>(args)...}};
   1025 }
   1026 
   1027 inline gfx::ColorSpace2 ToColorSpace2ForOutput(
   1028    const std::optional<dom::PredefinedColorSpace> chosenCspace) {
   1029  const auto cmsMode = GfxColorManagementMode();
   1030  switch (cmsMode) {
   1031    case CMSMode::Off:
   1032      return gfx::ColorSpace2::Display;
   1033    case CMSMode::TaggedOnly:
   1034      if (!chosenCspace) {
   1035        return gfx::ColorSpace2::Display;
   1036      }
   1037      break;
   1038    case CMSMode::All:
   1039      if (!chosenCspace) {
   1040        return gfx::ColorSpace2::SRGB;
   1041      }
   1042      break;
   1043  }
   1044  return gfx::ToColorSpace2(*chosenCspace);
   1045 }
   1046 
   1047 // -
   1048 
   1049 template <class T>
   1050 GLuint GLNameOrZero(const T& t) {
   1051  if (t) return t->mGLName;
   1052  return 0;
   1053 }
   1054 
   1055 // For an overview of how WebGL compositing works, see:
   1056 // https://wiki.mozilla.org/Platform/GFX/WebGL/Compositing
   1057 bool WebGLContext::PresentInto(gl::SwapChain& swapChain) {
   1058  OnEndOfFrame();
   1059 
   1060  if (!ValidateAndInitFB(nullptr)) return false;
   1061 
   1062  const auto size = mDefaultFB->mSize;
   1063 
   1064  const auto error = [&]() -> std::optional<std::string> {
   1065    const auto canvasCspace = ToColorSpace2ForOutput(mDrawingBufferColorSpace);
   1066    auto presenter = swapChain.Acquire(size, canvasCspace);
   1067    if (!presenter) {
   1068      return "Swap chain surface creation failed.";
   1069    }
   1070    const auto outputCspace = presenter->BackBuffer()->mDesc.colorSpace;
   1071    const auto destFb = presenter->Fb();
   1072 
   1073    // -
   1074 
   1075    bool colorManage = (canvasCspace != gfx::ColorSpace2::Display);
   1076    if (canvasCspace == outputCspace) {
   1077      colorManage = false;
   1078    }
   1079    if (!gl->IsSupported(gl::GLFeature::texture_3D)) {
   1080      NS_WARNING("Missing GLFeature::texture_3D => colorManage = false.");
   1081      colorManage = false;
   1082    }
   1083 
   1084    auto colorLut = std::shared_ptr<gl::Texture>{};
   1085    if (colorManage) {
   1086      MOZ_ASSERT(canvasCspace != gfx::ColorSpace2::Display);
   1087      colorLut = gl->BlitHelper()->GetColorLutTex(gl::GLBlitHelper::ColorLutKey{
   1088          .src = canvasCspace, .dst = outputCspace});
   1089      if (!colorLut) {
   1090        NS_WARNING("GetColorLutTex() -> nullptr => colorManage = false.");
   1091        colorManage = false;
   1092      }
   1093    }
   1094 
   1095    if (!colorManage) {
   1096      gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, destFb);
   1097      BlitBackbufferToCurDriverFB();
   1098      return {};
   1099    }
   1100 
   1101    // -
   1102 
   1103    const auto canvasFb = GetDefaultFBForRead({.endOfFrame = true});
   1104    if (!canvasFb) {
   1105      return "[WebGLContext::PresentInto] BindDefaultFBForRead failed.";
   1106    }
   1107 
   1108    const auto& blitter = gl->BlitHelper()->GetDrawBlitProg({
   1109        .fragHeader = gl::kFragHeader_Tex2D,
   1110        .fragParts = {gl::kFragSample_OnePlane, gl::kFragConvert_ColorLut3d},
   1111    });
   1112 
   1113    constexpr uint8_t texUnit_src = 0;
   1114    constexpr uint8_t texUnit_lut = 1;
   1115    gl->BindSamplerTexture(texUnit_src, SamplerLinear(), LOCAL_GL_TEXTURE_2D,
   1116                           canvasFb->ColorTex());
   1117    gl->BindSamplerTexture(texUnit_lut, SamplerLinear(), LOCAL_GL_TEXTURE_3D,
   1118                           colorLut->name);
   1119    const auto texCleanup = MakeScopeExit([&]() {
   1120      gl->BindSamplerTexture(
   1121          texUnit_src, GLNameOrZero(mBoundSamplers[texUnit_src]),
   1122          LOCAL_GL_TEXTURE_2D, GLNameOrZero(mBound2DTextures[texUnit_src]));
   1123      gl->BindSamplerTexture(
   1124          texUnit_lut, GLNameOrZero(mBoundSamplers[texUnit_lut]),
   1125          LOCAL_GL_TEXTURE_3D, GLNameOrZero(mBound3DTextures[texUnit_lut]));
   1126      gl->fActiveTexture(LOCAL_GL_TEXTURE0 + mActiveTexture);
   1127    });
   1128 
   1129    gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, destFb);
   1130 
   1131    gl->fUseProgram(blitter.mProg);
   1132    const auto cleanupProg = MakeScopeExit(
   1133        [&]() { gl->fUseProgram(GLNameOrZero(mCurrentProgram)); });
   1134 
   1135    gl->fUniform1i(blitter.mLoc_uColorLut, texUnit_lut);
   1136 
   1137    blitter.Draw({
   1138        .texMatrix0 = gl::Mat3::I(),
   1139        .yFlip = false,
   1140        .fbSize = size,
   1141        .destRect = {},
   1142    });
   1143 
   1144    gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, canvasFb->mFB);
   1145    return {};
   1146  }();
   1147  if (error) {
   1148    GenerateWarning("%s", error->c_str());
   1149    LoseContext();
   1150    return false;
   1151  }
   1152 
   1153  if (!mOptions.preserveDrawingBuffer) {
   1154    gl->InvalidateFramebuffer(LOCAL_GL_READ_FRAMEBUFFER);
   1155    mDefaultFB_IsInvalid = true;
   1156  }
   1157 
   1158  return true;
   1159 }
   1160 
   1161 bool WebGLContext::PresentIntoXR(gl::SwapChain& swapChain,
   1162                                 const gl::MozFramebuffer& fb) {
   1163  OnEndOfFrame();
   1164 
   1165  const auto colorSpace = ToColorSpace2ForOutput(mDrawingBufferColorSpace);
   1166  auto presenter = swapChain.Acquire(fb.mSize, colorSpace);
   1167  if (!presenter) {
   1168    GenerateWarning("Swap chain surface creation failed.");
   1169    LoseContext();
   1170    return false;
   1171  }
   1172 
   1173  const auto destFb = presenter->Fb();
   1174  gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, destFb);
   1175 
   1176  BlitBackbufferToCurDriverFB(nullptr, &fb);
   1177 
   1178  // https://immersive-web.github.io/webxr/#opaque-framebuffer
   1179  // Opaque framebuffers will always be cleared regardless of the
   1180  // associated WebGL context’s preserveDrawingBuffer value.
   1181  if (gl->IsSupported(gl::GLFeature::invalidate_framebuffer)) {
   1182    gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, fb.mFB);
   1183    constexpr auto attachments = MakeArray<GLenum>(
   1184        LOCAL_GL_COLOR_ATTACHMENT0, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT);
   1185    gl->fInvalidateFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, attachments.size(),
   1186                               attachments.data());
   1187  }
   1188 
   1189  return true;
   1190 }
   1191 
   1192 // Initialize a swap chain's surface factory given the desired surface type.
   1193 void InitSwapChain(gl::GLContext& gl, gl::SwapChain& swapChain,
   1194                   const layers::TextureType consumerType, bool useAsync) {
   1195  if (!swapChain.mFactory) {
   1196    auto typedFactory = gl::SurfaceFactory::Create(&gl, consumerType);
   1197    if (typedFactory) {
   1198      swapChain.mFactory = std::move(typedFactory);
   1199    }
   1200  }
   1201  if (!swapChain.mFactory) {
   1202    NS_WARNING("Failed to make an ideal SurfaceFactory.");
   1203    swapChain.mFactory = MakeUnique<gl::SurfaceFactory_Basic>(gl);
   1204  }
   1205  MOZ_ASSERT(swapChain.mFactory);
   1206  if (useAsync) {
   1207    // RemoteTextureMap will handle recycling any surfaces, so don't rely on the
   1208    // SwapChain's internal pooling.
   1209    swapChain.DisablePool();
   1210  }
   1211 }
   1212 
   1213 void WebGLContext::Present(WebGLFramebuffer* const xrFb,
   1214                           const layers::TextureType consumerType,
   1215                           const bool webvr,
   1216                           const webgl::SwapChainOptions& options) {
   1217  const FuncScope funcScope(*this, "<Present>");
   1218  if (IsContextLost()) {
   1219    EnsureContextLostRemoteTextureOwner(options);
   1220    return;
   1221  }
   1222 
   1223  auto swapChain = GetSwapChain(xrFb, webvr);
   1224  const gl::MozFramebuffer* maybeFB = nullptr;
   1225  if (xrFb) {
   1226    maybeFB = xrFb->mOpaque.get();
   1227  } else {
   1228    mResolvedDefaultFB = nullptr;
   1229  }
   1230 
   1231  bool useAsync = options.remoteTextureOwnerId.IsValid() &&
   1232                  options.remoteTextureId.IsValid();
   1233 
   1234  InitSwapChain(*gl, *swapChain, consumerType, useAsync);
   1235 
   1236  bool valid =
   1237      maybeFB ? PresentIntoXR(*swapChain, *maybeFB) : PresentInto(*swapChain);
   1238  if (!valid) {
   1239    EnsureContextLostRemoteTextureOwner(options);
   1240    return;
   1241  }
   1242 
   1243  if (useAsync) {
   1244    PushRemoteTexture(nullptr, *swapChain, swapChain->FrontBuffer(), options);
   1245  }
   1246 }
   1247 
   1248 void WebGLContext::WaitForTxn(layers::RemoteTextureOwnerId ownerId,
   1249                              layers::RemoteTextureTxnType txnType,
   1250                              layers::RemoteTextureTxnId txnId) {
   1251  if (!ownerId.IsValid() || !txnType || !txnId) {
   1252    return;
   1253  }
   1254  if (mRemoteTextureOwner && mRemoteTextureOwner->IsRegistered(ownerId)) {
   1255    mRemoteTextureOwner->WaitForTxn(ownerId, txnType, txnId);
   1256  }
   1257 }
   1258 
   1259 bool WebGLContext::CopyToSwapChain(
   1260    WebGLFramebuffer* const srcFb, const layers::TextureType consumerType,
   1261    const webgl::SwapChainOptions& options,
   1262    layers::RemoteTextureOwnerClient* ownerClient) {
   1263  const FuncScope funcScope(*this, "<CopyToSwapChain>");
   1264  if (IsContextLost()) {
   1265    return false;
   1266  }
   1267 
   1268  OnEndOfFrame();
   1269 
   1270  if (!srcFb || !srcFb->IsCheckFramebufferStatusComplete()) {
   1271    return false;
   1272  }
   1273  const auto* info = srcFb->GetCompletenessInfo();
   1274  if (!info) {
   1275    return false;
   1276  }
   1277  gfx::IntSize size(info->width, info->height);
   1278 
   1279  bool useAsync = options.remoteTextureOwnerId.IsValid() &&
   1280                  options.remoteTextureId.IsValid();
   1281 
   1282  InitSwapChain(*gl, srcFb->mSwapChain, consumerType, useAsync);
   1283 
   1284  // If we're using async present and if there is no way to serialize surfaces,
   1285  // then a readback is required to do the copy. In this case, there's no reason
   1286  // to copy into a separate shared surface for the front buffer. Just directly
   1287  // read back the WebGL framebuffer into and push it as a remote texture.
   1288  if (useAsync && srcFb->mSwapChain.mFactory->GetConsumerType() ==
   1289                      layers::TextureType::Unknown) {
   1290    return PushRemoteTexture(srcFb, srcFb->mSwapChain, nullptr, options,
   1291                             ownerClient);
   1292  }
   1293 
   1294  {
   1295    // TODO: ColorSpace will need to be part of SwapChainOptions for DTWebgl.
   1296    const auto colorSpace = ToColorSpace2ForOutput(mDrawingBufferColorSpace);
   1297    auto presenter = srcFb->mSwapChain.Acquire(size, colorSpace);
   1298    if (!presenter) {
   1299      GenerateWarning("Swap chain surface creation failed.");
   1300      LoseContext();
   1301      return false;
   1302    }
   1303 
   1304    const ScopedFBRebinder saveFB(this);
   1305 
   1306    const auto destFb = presenter->Fb();
   1307    gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, destFb);
   1308 
   1309    BlitBackbufferToCurDriverFB(srcFb, nullptr, options.bgra);
   1310  }
   1311 
   1312  if (useAsync) {
   1313    return PushRemoteTexture(srcFb, srcFb->mSwapChain,
   1314                             srcFb->mSwapChain.FrontBuffer(), options,
   1315                             ownerClient);
   1316  }
   1317  return true;
   1318 }
   1319 
   1320 bool WebGLContext::PushRemoteTexture(
   1321    WebGLFramebuffer* fb, gl::SwapChain& swapChain,
   1322    std::shared_ptr<gl::SharedSurface> surf,
   1323    const webgl::SwapChainOptions& options,
   1324    layers::RemoteTextureOwnerClient* ownerClient) {
   1325  const auto onFailure = [&]() -> bool {
   1326    GenerateWarning("Remote texture creation failed.");
   1327    LoseContext();
   1328    if (ownerClient && ownerClient == mRemoteTextureOwner) {
   1329      ownerClient->PushDummyTexture(options.remoteTextureId,
   1330                                    options.remoteTextureOwnerId);
   1331    }
   1332    return false;
   1333  };
   1334 
   1335  if (!ownerClient) {
   1336    if (!mRemoteTextureOwner) {
   1337      // Ensure we have a remote texture owner client for WebGLParent.
   1338      const auto* outOfProcess =
   1339          mHost ? mHost->mOwnerData.outOfProcess : nullptr;
   1340      if (!outOfProcess) {
   1341        return onFailure();
   1342      }
   1343      auto pid = outOfProcess->OtherPid();
   1344      mRemoteTextureOwner = MakeRefPtr<layers::RemoteTextureOwnerClient>(pid);
   1345    }
   1346    ownerClient = mRemoteTextureOwner;
   1347  }
   1348 
   1349  layers::RemoteTextureOwnerId ownerId = options.remoteTextureOwnerId;
   1350  layers::RemoteTextureId textureId = options.remoteTextureId;
   1351 
   1352  if (!ownerClient->IsRegistered(ownerId)) {
   1353    // Register a texture owner to represent the swap chain.
   1354    RefPtr<layers::RemoteTextureOwnerClient> textureOwner = ownerClient;
   1355    auto destroyedCallback = [textureOwner, ownerId]() {
   1356      textureOwner->UnregisterTextureOwner(ownerId);
   1357    };
   1358 
   1359    swapChain.SetDestroyedCallback(destroyedCallback);
   1360    ownerClient->RegisterTextureOwner(ownerId,
   1361                                      /* aSharedRecycling */ !!fb);
   1362  }
   1363 
   1364  MOZ_ASSERT(fb || surf);
   1365  gfx::IntSize size;
   1366  if (surf) {
   1367    size = surf->mDesc.size;
   1368  } else {
   1369    const auto* info = fb->GetCompletenessInfo();
   1370    MOZ_ASSERT(info);
   1371    size = gfx::IntSize(info->width, info->height);
   1372  }
   1373 
   1374  const auto surfaceFormat = mOptions.alpha ? gfx::SurfaceFormat::B8G8R8A8
   1375                                            : gfx::SurfaceFormat::B8G8R8X8;
   1376  Maybe<layers::SurfaceDescriptor> desc;
   1377  if (surf) {
   1378    desc = surf->ToSurfaceDescriptor();
   1379  }
   1380  if (!desc) {
   1381    if (surf && surf->mDesc.type != gl::SharedSurfaceType::Basic) {
   1382      return onFailure();
   1383    }
   1384    // If we can't serialize to a surface descriptor, then we need to create
   1385    // a buffer to read back into that will become the remote texture.
   1386    auto data = ownerClient->CreateOrRecycleBufferTextureData(
   1387        size, surfaceFormat, ownerId);
   1388    if (!data) {
   1389      gfxCriticalNoteOnce << "Failed to allocate BufferTextureData";
   1390      return onFailure();
   1391    }
   1392 
   1393    layers::MappedTextureData mappedData;
   1394    if (!data->BorrowMappedData(mappedData)) {
   1395      return onFailure();
   1396    }
   1397 
   1398    Range<uint8_t> range = {mappedData.data,
   1399                            data->AsBufferTextureData()->GetBufferSize()};
   1400 
   1401    // If we have a surface representing the front buffer, then try to snapshot
   1402    // that. Otherwise, when there is no surface, we read back directly from the
   1403    // WebGL framebuffer.
   1404    auto valid =
   1405        surf ? FrontBufferSnapshotInto(surf, Some(range),
   1406                                       Some(mappedData.stride))
   1407             : SnapshotInto(fb->mGLName, size, range, Some(mappedData.stride));
   1408    if (!valid) {
   1409      return onFailure();
   1410    }
   1411 
   1412    if (!options.bgra) {
   1413      // If the buffer is already BGRA, we don't need to swizzle. However, if it
   1414      // is RGBA, then a swizzle to BGRA is required.
   1415      bool rv = gfx::SwizzleData(mappedData.data, mappedData.stride,
   1416                                 gfx::SurfaceFormat::R8G8B8A8, mappedData.data,
   1417                                 mappedData.stride,
   1418                                 gfx::SurfaceFormat::B8G8R8A8, mappedData.size);
   1419      MOZ_RELEASE_ASSERT(rv, "SwizzleData failed!");
   1420    }
   1421 
   1422    ownerClient->PushTexture(textureId, ownerId, std::move(data));
   1423    return true;
   1424  }
   1425 
   1426  // SharedSurfaces of SurfaceDescriptorD3D10 and SurfaceDescriptorMacIOSurface
   1427  // need to be kept alive. They will be recycled by
   1428  // RemoteTextureOwnerClient::GetRecycledSharedSurface() when their usages are
   1429  // ended.
   1430  std::shared_ptr<gl::SharedSurface> keepAlive;
   1431  switch (desc->type()) {
   1432    case layers::SurfaceDescriptor::TSurfaceDescriptorD3D10:
   1433    case layers::SurfaceDescriptor::TSurfaceDescriptorMacIOSurface:
   1434    case layers::SurfaceDescriptor::TSurfaceTextureDescriptor:
   1435    case layers::SurfaceDescriptor::TSurfaceDescriptorAndroidHardwareBuffer:
   1436    case layers::SurfaceDescriptor::TEGLImageDescriptor:
   1437    case layers::SurfaceDescriptor::TSurfaceDescriptorDMABuf:
   1438      keepAlive = surf;
   1439      break;
   1440    default:
   1441      break;
   1442  }
   1443 
   1444  ownerClient->PushTexture(textureId, ownerId, keepAlive, size, surfaceFormat,
   1445                           *desc);
   1446 
   1447  // Look for a recycled surface that matches the swap chain.
   1448  while (auto recycledSurface = ownerClient->GetRecycledSharedSurface(
   1449             size, surfaceFormat, desc->type(), ownerId)) {
   1450    if (swapChain.StoreRecycledSurface(recycledSurface)) {
   1451      break;
   1452    }
   1453  }
   1454  return true;
   1455 }
   1456 
   1457 void WebGLContext::EnsureContextLostRemoteTextureOwner(
   1458    const webgl::SwapChainOptions& options) {
   1459  if (!options.remoteTextureOwnerId.IsValid()) {
   1460    return;
   1461  }
   1462 
   1463  if (!mRemoteTextureOwner) {
   1464    // Ensure we have a remote texture owner client for WebGLParent.
   1465    const auto* outOfProcess = mHost ? mHost->mOwnerData.outOfProcess : nullptr;
   1466    if (!outOfProcess) {
   1467      return;
   1468    }
   1469    auto pid = outOfProcess->OtherPid();
   1470    mRemoteTextureOwner = MakeRefPtr<layers::RemoteTextureOwnerClient>(pid);
   1471  }
   1472 
   1473  layers::RemoteTextureOwnerId ownerId = options.remoteTextureOwnerId;
   1474 
   1475  if (!mRemoteTextureOwner->IsRegistered(ownerId)) {
   1476    mRemoteTextureOwner->RegisterTextureOwner(ownerId);
   1477  }
   1478  mRemoteTextureOwner->NotifyContextLost();
   1479 }
   1480 
   1481 void WebGLContext::EndOfFrame() {
   1482  const FuncScope funcScope(*this, "<EndOfFrame>");
   1483  if (IsContextLost()) return;
   1484 
   1485  OnEndOfFrame();
   1486 }
   1487 
   1488 gl::SwapChain* WebGLContext::GetSwapChain(WebGLFramebuffer* const xrFb,
   1489                                          const bool webvr) {
   1490  auto swapChain = webvr ? &mWebVRSwapChain : &mSwapChain;
   1491  if (xrFb) {
   1492    swapChain = &xrFb->mSwapChain;
   1493  }
   1494  return swapChain;
   1495 }
   1496 
   1497 Maybe<layers::SurfaceDescriptor> WebGLContext::GetFrontBuffer(
   1498    WebGLFramebuffer* const xrFb, const bool webvr) {
   1499  auto* swapChain = GetSwapChain(xrFb, webvr);
   1500  if (!swapChain) return {};
   1501  const auto& front = swapChain->FrontBuffer();
   1502  if (!front) return {};
   1503 
   1504  return front->ToSurfaceDescriptor();
   1505 }
   1506 
   1507 Maybe<uvec2> WebGLContext::FrontBufferSnapshotInto(
   1508    const Maybe<Range<uint8_t>> maybeDest, const Maybe<size_t> destStride) {
   1509  const auto& front = mSwapChain.FrontBuffer();
   1510  if (!front) return {};
   1511  return FrontBufferSnapshotInto(front, maybeDest, destStride);
   1512 }
   1513 
   1514 Maybe<uvec2> WebGLContext::FrontBufferSnapshotInto(
   1515    const std::shared_ptr<gl::SharedSurface>& front,
   1516    const Maybe<Range<uint8_t>> maybeDest, const Maybe<size_t> destStride) {
   1517  const auto& size = front->mDesc.size;
   1518  if (!maybeDest) return Some(*uvec2::FromSize(size));
   1519 
   1520  // -
   1521 
   1522  front->BeginRead();
   1523  auto reset = MakeScopeExit([&] { front->EndRead(); });
   1524 
   1525  // -
   1526 
   1527  return SnapshotInto(front->mFb ? front->mFb->mFB : 0, size, *maybeDest,
   1528                      destStride);
   1529 }
   1530 
   1531 Maybe<uvec2> WebGLContext::SnapshotInto(GLuint srcFb, const gfx::IntSize& size,
   1532                                        const Range<uint8_t>& dest,
   1533                                        const Maybe<size_t> destStride) {
   1534  const auto minStride = CheckedInt<size_t>(size.width) * 4;
   1535  if (!minStride.isValid()) {
   1536    gfxCriticalError() << "SnapshotInto: invalid stride, width:" << size.width;
   1537    return {};
   1538  }
   1539  size_t stride = destStride.valueOr(minStride.value());
   1540  if (stride < minStride.value() || (stride % 4) != 0) {
   1541    gfxCriticalError() << "SnapshotInto: invalid stride, width:" << size.width
   1542                       << ", stride:" << stride;
   1543    return {};
   1544  }
   1545 
   1546  gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, 1);
   1547  if (IsWebGL2()) {
   1548    gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH,
   1549                     stride > minStride.value() ? stride / 4 : 0);
   1550    gl->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, 0);
   1551    gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, 0);
   1552  }
   1553 
   1554  // -
   1555 
   1556  const auto readFbWas = mBoundReadFramebuffer;
   1557  const auto pboWas = mBoundPixelPackBuffer;
   1558 
   1559  GLenum fbTarget = LOCAL_GL_READ_FRAMEBUFFER;
   1560  if (!IsWebGL2()) {
   1561    fbTarget = LOCAL_GL_FRAMEBUFFER;
   1562  }
   1563  auto reset2 = MakeScopeExit([&] {
   1564    DoBindFB(readFbWas, fbTarget);
   1565    if (pboWas) {
   1566      BindBuffer(LOCAL_GL_PIXEL_PACK_BUFFER, pboWas);
   1567    }
   1568  });
   1569 
   1570  gl->fBindFramebuffer(fbTarget, srcFb);
   1571  if (pboWas) {
   1572    BindBuffer(LOCAL_GL_PIXEL_PACK_BUFFER, nullptr);
   1573  }
   1574 
   1575  // -
   1576 
   1577  const auto srcByteCount = CheckedInt<size_t>(stride) * size.height;
   1578  if (!srcByteCount.isValid()) {
   1579    gfxCriticalError() << "SnapshotInto: invalid srcByteCount, width:"
   1580                       << size.width << ", height:" << size.height;
   1581    return {};
   1582  }
   1583  const auto dstByteCount = dest.length();
   1584  if (srcByteCount.value() > dstByteCount) {
   1585    gfxCriticalError() << "SnapshotInto: srcByteCount:" << srcByteCount.value()
   1586                       << " > dstByteCount:" << dstByteCount;
   1587    return {};
   1588  }
   1589  uint8_t* dstPtr = dest.begin().get();
   1590  gl->fReadPixels(0, 0, size.width, size.height, LOCAL_GL_RGBA,
   1591                  LOCAL_GL_UNSIGNED_BYTE, dstPtr);
   1592 
   1593  if (!IsWebGL2() && stride > minStride.value() && size.height > 1) {
   1594    // WebGL 1 doesn't support PACK_ROW_LENGTH. Instead, we read the data tight
   1595    // into the front of the buffer, and use memmove (since the source and dest
   1596    // may overlap) starting from the back to move it to the correct stride
   1597    // offsets. We don't move the first row as it is already in the right place.
   1598    uint8_t* destRow = dstPtr + stride * (size.height - 1);
   1599    const uint8_t* srcRow = dstPtr + minStride.value() * (size.height - 1);
   1600    while (destRow > dstPtr) {
   1601      memmove(destRow, srcRow, minStride.value());
   1602      destRow -= stride;
   1603      srcRow -= minStride.value();
   1604    }
   1605  }
   1606 
   1607  return Some(*uvec2::FromSize(size));
   1608 }
   1609 
   1610 already_AddRefed<gfx::SourceSurface> WebGLContext::GetBackBufferSnapshot(
   1611    const bool requireAlphaPremult) {
   1612  if (IsContextLost()) {
   1613    return nullptr;
   1614  }
   1615 
   1616  const auto surfSize = DrawingBufferSize();
   1617  if (surfSize.x <= 0 || surfSize.y <= 0) {
   1618    return nullptr;
   1619  }
   1620 
   1621  const auto surfFormat = mOptions.alpha ? gfx::SurfaceFormat::B8G8R8A8
   1622                                         : gfx::SurfaceFormat::B8G8R8X8;
   1623 
   1624  RefPtr<gfx::DataSourceSurface> dataSurf =
   1625      gfx::Factory::CreateDataSourceSurface(
   1626          gfx::IntSize(surfSize.x, surfSize.y), surfFormat);
   1627  if (!dataSurf) {
   1628    NS_WARNING("Failed to alloc DataSourceSurface for GetBackBufferSnapshot");
   1629    return nullptr;
   1630  }
   1631 
   1632  {
   1633    gfx::DataSourceSurface::ScopedMap map(dataSurf,
   1634                                          gfx::DataSourceSurface::READ_WRITE);
   1635    if (!map.IsMapped()) {
   1636      NS_WARNING("Failed to map DataSourceSurface for GetBackBufferSnapshot");
   1637      return nullptr;
   1638    }
   1639 
   1640    // GetDefaultFBForRead might overwrite FB state if it needs to resolve a
   1641    // multisampled FB, so save/restore the FB state here just in case.
   1642    const gl::ScopedBindFramebuffer bindFb(gl);
   1643    const auto fb = GetDefaultFBForRead();
   1644    if (!fb) {
   1645      gfxCriticalNote << "GetDefaultFBForRead failed for GetBackBufferSnapshot";
   1646      return nullptr;
   1647    }
   1648    const auto byteCount = CheckedInt<size_t>(map.GetStride()) * surfSize.y;
   1649    if (!byteCount.isValid()) {
   1650      gfxCriticalNote << "Invalid byte count for GetBackBufferSnapshot";
   1651      return nullptr;
   1652    }
   1653    const Range<uint8_t> range = {map.GetData(), byteCount.value()};
   1654    if (!SnapshotInto(fb->mFB, fb->mSize, range,
   1655                      Some(size_t(map.GetStride())))) {
   1656      gfxCriticalNote << "SnapshotInto failed for GetBackBufferSnapshot";
   1657      return nullptr;
   1658    }
   1659 
   1660    if (requireAlphaPremult && mOptions.alpha && !mOptions.premultipliedAlpha) {
   1661      bool rv = gfx::PremultiplyYFlipData(
   1662          map.GetData(), map.GetStride(), gfx::SurfaceFormat::R8G8B8A8,
   1663          map.GetData(), map.GetStride(), surfFormat, dataSurf->GetSize());
   1664      MOZ_RELEASE_ASSERT(rv, "PremultiplyYFlipData failed!");
   1665    } else {
   1666      bool rv = gfx::SwizzleYFlipData(
   1667          map.GetData(), map.GetStride(), gfx::SurfaceFormat::R8G8B8A8,
   1668          map.GetData(), map.GetStride(), surfFormat, dataSurf->GetSize());
   1669      MOZ_RELEASE_ASSERT(rv, "SwizzleYFlipData failed!");
   1670    }
   1671  }
   1672 
   1673  return dataSurf.forget();
   1674 }
   1675 
   1676 std::shared_ptr<gl::SharedSurface>
   1677 WebGLContext::GetBackBufferSnapshotSharedSurface(layers::TextureType texType,
   1678                                                 bool bgra, bool yFlip,
   1679                                                 bool requireAlphaPremult) {
   1680  const FuncScope funcScope(*this, "<GetBackBufferSnapshotSharedSurface>");
   1681  if (IsContextLost()) {
   1682    return nullptr;
   1683  }
   1684 
   1685  const auto surfSize = DrawingBufferSize();
   1686  if (surfSize.x <= 0 || surfSize.y <= 0) {
   1687    return nullptr;
   1688  }
   1689 
   1690  InitSwapChain(*gl, mSnapshotSwapChain, texType, true);
   1691 
   1692  {
   1693    // TODO: ColorSpace will need to be part of SwapChainOptions for DTWebgl.
   1694    const auto colorSpace = ToColorSpace2ForOutput(mDrawingBufferColorSpace);
   1695    auto presenter = mSnapshotSwapChain.Acquire(
   1696        gfx::IntSize(surfSize.x, surfSize.y), colorSpace);
   1697    if (!presenter) {
   1698      GenerateWarning("Swap chain surface creation failed.");
   1699      return nullptr;
   1700    }
   1701 
   1702    const ScopedFBRebinder saveFB(this);
   1703    const auto srcFb = GetDefaultFBForRead();
   1704 
   1705    const auto destFb = presenter->Fb();
   1706    gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, destFb);
   1707 
   1708    BlitBackbufferToCurDriverFB(
   1709        nullptr, srcFb, bgra, yFlip,
   1710        requireAlphaPremult && mOptions.alpha && !mOptions.premultipliedAlpha
   1711            ? Some(gfxAlphaType::Premult)
   1712            : Nothing());
   1713  }
   1714 
   1715  return mSnapshotSwapChain.FrontBuffer();
   1716 }
   1717 
   1718 void WebGLContext::RecycleSnapshotSharedSurface(
   1719    std::shared_ptr<gl::SharedSurface> sharedSurface) {
   1720  mSnapshotSwapChain.StoreRecycledSurface(sharedSurface);
   1721 }
   1722 
   1723 void WebGLContext::ClearVRSwapChain() { mWebVRSwapChain.ClearPool(); }
   1724 
   1725 // ------------------------
   1726 
   1727 RefPtr<gfx::DataSourceSurface> GetTempSurface(const gfx::IntSize& aSize,
   1728                                              gfx::SurfaceFormat& aFormat) {
   1729  uint32_t stride =
   1730      gfx::GetAlignedStride<8>(aSize.width, BytesPerPixel(aFormat));
   1731  return gfx::Factory::CreateDataSourceSurfaceWithStride(aSize, aFormat,
   1732                                                         stride);
   1733 }
   1734 
   1735 void WebGLContext::DummyReadFramebufferOperation() {
   1736  if (!mBoundReadFramebuffer) return;  // Infallible.
   1737 
   1738  const auto status = mBoundReadFramebuffer->CheckFramebufferStatus();
   1739  if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
   1740    ErrorInvalidFramebufferOperation("Framebuffer must be complete.");
   1741  }
   1742 }
   1743 
   1744 layers::SharedSurfacesHolder* WebGLContext::GetSharedSurfacesHolder() const {
   1745  const auto* outOfProcess = mHost ? mHost->mOwnerData.outOfProcess : nullptr;
   1746  if (outOfProcess) {
   1747    return outOfProcess->mSharedSurfacesHolder;
   1748  }
   1749  MOZ_ASSERT_UNREACHABLE("Unexpected use of SharedSurfacesHolder in process!");
   1750  return nullptr;
   1751 }
   1752 
   1753 dom::ContentParentId WebGLContext::GetContentId() const {
   1754  const auto* outOfProcess = mHost ? mHost->mOwnerData.outOfProcess : nullptr;
   1755  if (outOfProcess) {
   1756    return outOfProcess->mContentId;
   1757  }
   1758  if (XRE_IsContentProcess()) {
   1759    return dom::ContentChild::GetSingleton()->GetID();
   1760  }
   1761  return dom::ContentParentId();
   1762 }
   1763 
   1764 bool WebGLContext::Has64BitTimestamps() const {
   1765  // 'sync' provides glGetInteger64v either by supporting ARB_sync, GL3+, or
   1766  // GLES3+.
   1767  return gl->IsSupported(gl::GLFeature::sync);
   1768 }
   1769 
   1770 static bool CheckContextLost(gl::GLContext* gl, bool* const out_isGuilty) {
   1771  MOZ_ASSERT(gl);
   1772 
   1773  const auto resetStatus = gl->fGetGraphicsResetStatus();
   1774  if (resetStatus == LOCAL_GL_NO_ERROR) {
   1775    *out_isGuilty = false;
   1776    return false;
   1777  }
   1778 
   1779  // Assume guilty unless we find otherwise!
   1780  bool isGuilty = true;
   1781  switch (resetStatus) {
   1782    case LOCAL_GL_INNOCENT_CONTEXT_RESET_ARB:
   1783    case LOCAL_GL_PURGED_CONTEXT_RESET_NV:
   1784      // Either nothing wrong, or not our fault.
   1785      isGuilty = false;
   1786      break;
   1787    case LOCAL_GL_GUILTY_CONTEXT_RESET_ARB:
   1788      NS_WARNING(
   1789          "WebGL content on the page definitely caused the graphics"
   1790          " card to reset.");
   1791      break;
   1792    case LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB:
   1793      NS_WARNING(
   1794          "WebGL content on the page might have caused the graphics"
   1795          " card to reset");
   1796      // If we can't tell, assume not-guilty.
   1797      // Todo: Implement max number of "unknown" resets per document or time.
   1798      isGuilty = false;
   1799      break;
   1800    default:
   1801      gfxCriticalError() << "Unexpected glGetGraphicsResetStatus: "
   1802                         << gfx::hexa(resetStatus);
   1803      break;
   1804  }
   1805 
   1806  if (isGuilty) {
   1807    NS_WARNING(
   1808        "WebGL context on this page is considered guilty, and will"
   1809        " not be restored.");
   1810  }
   1811 
   1812  *out_isGuilty = isGuilty;
   1813  return true;
   1814 }
   1815 
   1816 void WebGLContext::RunContextLossTimer() { mContextLossHandler.RunTimer(); }
   1817 
   1818 // We use this timer for many things. Here are the things that it is activated
   1819 // for:
   1820 // 1) If a script is using the MOZ_WEBGL_lose_context extension.
   1821 // 2) If we are using EGL and _NOT ANGLE_, we query periodically to see if the
   1822 //    CONTEXT_LOST_WEBGL error has been triggered.
   1823 // 3) If we are using ANGLE, or anything that supports ARB_robustness, query the
   1824 //    GPU periodically to see if the reset status bit has been set.
   1825 // In all of these situations, we use this timer to send the script context lost
   1826 // and restored events asynchronously. For example, if it triggers a context
   1827 // loss, the webglcontextlost event will be sent to it the next time the
   1828 // robustness timer fires.
   1829 // Note that this timer mechanism is not used unless one of these 3 criteria are
   1830 // met.
   1831 // At a bare minimum, from context lost to context restores, it would take 3
   1832 // full timer iterations: detection, webglcontextlost, webglcontextrestored.
   1833 void WebGLContext::CheckForContextLoss() {
   1834  bool isGuilty = true;
   1835  const auto isContextLost = CheckContextLost(gl, &isGuilty);
   1836  if (!isContextLost) return;
   1837 
   1838  mWebGLError = LOCAL_GL_CONTEXT_LOST;
   1839 
   1840  auto reason = webgl::ContextLossReason::None;
   1841  if (isGuilty) {
   1842    reason = webgl::ContextLossReason::Guilty;
   1843  }
   1844  LoseContext(reason);
   1845 }
   1846 
   1847 void WebGLContext::HandlePendingContextLoss() {
   1848  mIsContextLost = true;
   1849  if (mHost) {
   1850    mHost->OnContextLoss(mPendingContextLossReason);
   1851  }
   1852 }
   1853 
   1854 void WebGLContext::LoseContextLruLocked(const webgl::ContextLossReason reason) {
   1855  printf_stderr("WebGL(%p)::LoseContext(%u)\n", this,
   1856                static_cast<uint32_t>(reason));
   1857  mLruPosition.ResetLocked();
   1858  mPendingContextLossReason = reason;
   1859  mPendingContextLoss = true;
   1860 }
   1861 
   1862 void WebGLContext::LoseContext(const webgl::ContextLossReason reason) {
   1863  StaticMutexAutoLock lock(sLruMutex);
   1864  LoseContextLruLocked(reason);
   1865  HandlePendingContextLoss();
   1866  if (mRemoteTextureOwner) {
   1867    mRemoteTextureOwner->NotifyContextLost();
   1868  }
   1869 }
   1870 
   1871 void WebGLContext::DidRefresh() {
   1872  if (gl) {
   1873    gl->FlushIfHeavyGLCallsSinceLastFlush();
   1874  }
   1875 }
   1876 
   1877 ////////////////////////////////////////////////////////////////////////////////
   1878 
   1879 uvec2 WebGLContext::DrawingBufferSize() {
   1880  const FuncScope funcScope(*this, "width/height");
   1881  if (IsContextLost()) return {};
   1882 
   1883  if (!EnsureDefaultFB()) return {};
   1884 
   1885  return *uvec2::FromSize(mDefaultFB->mSize);
   1886 }
   1887 
   1888 bool WebGLContext::ValidateAndInitFB(const WebGLFramebuffer* const fb,
   1889                                     const GLenum incompleteFbError) {
   1890  if (fb) return fb->ValidateAndInitAttachments(incompleteFbError);
   1891 
   1892  if (!EnsureDefaultFB()) return false;
   1893 
   1894  if (mDefaultFB_IsInvalid) {
   1895    // Clear it!
   1896    gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mDefaultFB->mFB);
   1897    const webgl::ScopedPrepForResourceClear scopedPrep(*this);
   1898    if (!mOptions.alpha) {
   1899      gl->fClearColor(0, 0, 0, 1);
   1900    }
   1901    const GLbitfield bits = LOCAL_GL_COLOR_BUFFER_BIT |
   1902                            LOCAL_GL_DEPTH_BUFFER_BIT |
   1903                            LOCAL_GL_STENCIL_BUFFER_BIT;
   1904    gl->fClear(bits);
   1905 
   1906    mDefaultFB_IsInvalid = false;
   1907  }
   1908  return true;
   1909 }
   1910 
   1911 void WebGLContext::DoBindFB(const WebGLFramebuffer* const fb,
   1912                            const GLenum target) const {
   1913  const GLenum driverFB = fb ? fb->mGLName : (mDefaultFB ? mDefaultFB->mFB : 0);
   1914  gl->fBindFramebuffer(target, driverFB);
   1915 }
   1916 
   1917 bool WebGLContext::BindCurFBForDraw() {
   1918  const auto& fb = mBoundDrawFramebuffer;
   1919  if (!ValidateAndInitFB(fb)) return false;
   1920 
   1921  DoBindFB(fb);
   1922  return true;
   1923 }
   1924 
   1925 bool WebGLContext::BindCurFBForColorRead(
   1926    const webgl::FormatUsageInfo** const out_format, uint32_t* const out_width,
   1927    uint32_t* const out_height, const GLenum incompleteFbError) {
   1928  const auto& fb = mBoundReadFramebuffer;
   1929 
   1930  if (fb) {
   1931    if (!ValidateAndInitFB(fb, incompleteFbError)) return false;
   1932    if (!fb->ValidateForColorRead(out_format, out_width, out_height))
   1933      return false;
   1934 
   1935    gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fb->mGLName);
   1936    return true;
   1937  }
   1938 
   1939  if (!BindDefaultFBForRead()) return false;
   1940 
   1941  if (mDefaultFB_ReadBuffer == LOCAL_GL_NONE) {
   1942    ErrorInvalidOperation(
   1943        "Can't read from backbuffer when readBuffer mode is NONE.");
   1944    return false;
   1945  }
   1946 
   1947  auto effFormat = mOptions.alpha ? webgl::EffectiveFormat::RGBA8
   1948                                  : webgl::EffectiveFormat::RGB8;
   1949 
   1950  *out_format = mFormatUsage->GetUsage(effFormat);
   1951  MOZ_ASSERT(*out_format);
   1952 
   1953  *out_width = mDefaultFB->mSize.width;
   1954  *out_height = mDefaultFB->mSize.height;
   1955  return true;
   1956 }
   1957 
   1958 const gl::MozFramebuffer* WebGLContext::GetDefaultFBForRead(
   1959    const GetDefaultFBForReadDesc& desc) {
   1960  if (!ValidateAndInitFB(nullptr)) return nullptr;
   1961 
   1962  if (!mDefaultFB->mSamples) {
   1963    return mDefaultFB.get();
   1964  }
   1965 
   1966  if (!mResolvedDefaultFB) {
   1967    mResolvedDefaultFB =
   1968        gl::MozFramebuffer::Create(gl, mDefaultFB->mSize, 0, false);
   1969    if (!mResolvedDefaultFB) {
   1970      gfxCriticalNote << FuncName() << ": Failed to create mResolvedDefaultFB.";
   1971      return nullptr;
   1972    }
   1973  }
   1974 
   1975  gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, mResolvedDefaultFB->mFB);
   1976  BlitBackbufferToCurDriverFB();
   1977 
   1978  if (desc.endOfFrame && !mOptions.preserveDrawingBuffer) {
   1979    gl->InvalidateFramebuffer(LOCAL_GL_READ_FRAMEBUFFER);
   1980  }
   1981 
   1982  return mResolvedDefaultFB.get();
   1983 }
   1984 
   1985 bool WebGLContext::BindDefaultFBForRead() {
   1986  const auto fb = GetDefaultFBForRead();
   1987  if (!fb) return false;
   1988  gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, fb->mFB);
   1989  return true;
   1990 }
   1991 
   1992 void WebGLContext::DoColorMask(Maybe<GLuint> i, const uint8_t bitmask) const {
   1993  if (!IsExtensionEnabled(WebGLExtensionID::OES_draw_buffers_indexed)) {
   1994    i = Nothing();
   1995  }
   1996  const auto bs = std::bitset<4>(bitmask);
   1997  if (i) {
   1998    gl->fColorMaski(*i, bs[0], bs[1], bs[2], bs[3]);
   1999  } else {
   2000    gl->fColorMask(bs[0], bs[1], bs[2], bs[3]);
   2001  }
   2002 }
   2003 
   2004 ////////////////////////////////////////////////////////////////////////////////
   2005 
   2006 ScopedDrawCallWrapper::ScopedDrawCallWrapper(WebGLContext& webgl)
   2007    : mWebGL(webgl) {
   2008  uint8_t driverColorMask0 = mWebGL.mColorWriteMask0;
   2009  bool driverDepthTest = mWebGL.mDepthTestEnabled;
   2010  bool driverStencilTest = mWebGL.mStencilTestEnabled;
   2011  const auto& fb = mWebGL.mBoundDrawFramebuffer;
   2012  if (!fb) {
   2013    if (mWebGL.mDefaultFB_DrawBuffer0 == LOCAL_GL_NONE) {
   2014      driverColorMask0 = 0;  // Is this well-optimized enough for depth-first
   2015                             // rendering?
   2016    } else {
   2017      driverColorMask0 &= ~(uint8_t(mWebGL.mNeedsFakeNoAlpha) << 3);
   2018    }
   2019    driverDepthTest &= !mWebGL.mNeedsFakeNoDepth;
   2020    driverStencilTest &= !mWebGL.mNeedsFakeNoStencil;
   2021  }
   2022 
   2023  const auto& gl = mWebGL.gl;
   2024  mWebGL.DoColorMask(Some(0), driverColorMask0);
   2025  if (mWebGL.mDriverDepthTest != driverDepthTest) {
   2026    // "When disabled, the depth comparison and subsequent possible updates to
   2027    // the
   2028    //  depth buffer value are bypassed and the fragment is passed to the next
   2029    //  operation." [GLES 3.0.5, p177]
   2030    mWebGL.mDriverDepthTest = driverDepthTest;
   2031    gl->SetEnabled(LOCAL_GL_DEPTH_TEST, mWebGL.mDriverDepthTest);
   2032  }
   2033  if (mWebGL.mDriverStencilTest != driverStencilTest) {
   2034    // "When disabled, the stencil test and associated modifications are not
   2035    // made, and
   2036    //  the fragment is always passed." [GLES 3.0.5, p175]
   2037    mWebGL.mDriverStencilTest = driverStencilTest;
   2038    gl->SetEnabled(LOCAL_GL_STENCIL_TEST, mWebGL.mDriverStencilTest);
   2039  }
   2040 }
   2041 
   2042 ScopedDrawCallWrapper::~ScopedDrawCallWrapper() {
   2043  if (mWebGL.mBoundDrawFramebuffer) return;
   2044 
   2045  mWebGL.mResolvedDefaultFB = nullptr;
   2046  mWebGL.mShouldPresent = true;
   2047 }
   2048 
   2049 // -
   2050 
   2051 void WebGLContext::ScissorRect::Apply(gl::GLContext& gl) const {
   2052  gl.fScissor(x, y, w, h);
   2053 }
   2054 
   2055 ////////////////////////////////////////
   2056 
   2057 IndexedBufferBinding::IndexedBufferBinding() = default;
   2058 IndexedBufferBinding::~IndexedBufferBinding() = default;
   2059 
   2060 uint64_t IndexedBufferBinding::ByteCount() const {
   2061  if (!mBufferBinding) return 0;
   2062 
   2063  uint64_t bufferSize = mBufferBinding->ByteLength();
   2064  if (!mRangeSize)  // BindBufferBase
   2065    return bufferSize;
   2066 
   2067  if (mRangeStart >= bufferSize) return 0;
   2068  bufferSize -= mRangeStart;
   2069 
   2070  return std::min(bufferSize, mRangeSize);
   2071 }
   2072 
   2073 ////////////////////////////////////////
   2074 
   2075 ScopedFBRebinder::~ScopedFBRebinder() {
   2076  const auto fnName = [&](WebGLFramebuffer* fb) {
   2077    return fb ? fb->mGLName : 0;
   2078  };
   2079 
   2080  const auto& gl = mWebGL->gl;
   2081  if (mWebGL->IsWebGL2()) {
   2082    gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER,
   2083                         fnName(mWebGL->mBoundDrawFramebuffer));
   2084    gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER,
   2085                         fnName(mWebGL->mBoundReadFramebuffer));
   2086  } else {
   2087    MOZ_ASSERT(mWebGL->mBoundDrawFramebuffer == mWebGL->mBoundReadFramebuffer);
   2088    gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER,
   2089                         fnName(mWebGL->mBoundDrawFramebuffer));
   2090  }
   2091 }
   2092 
   2093 ////////////////////
   2094 
   2095 void DoBindBuffer(gl::GLContext& gl, const GLenum target,
   2096                  const WebGLBuffer* const buffer) {
   2097  gl.fBindBuffer(target, buffer ? buffer->mGLName : 0);
   2098 }
   2099 
   2100 ////////////////////////////////////////
   2101 
   2102 bool Intersect(const int32_t srcSize, const int32_t read0,
   2103               const int32_t readSize, int32_t* const out_intRead0,
   2104               int32_t* const out_intWrite0, int32_t* const out_intSize) {
   2105  MOZ_ASSERT(srcSize >= 0);
   2106  MOZ_ASSERT(readSize >= 0);
   2107  const auto read1 = int64_t(read0) + readSize;
   2108 
   2109  int32_t intRead0 = read0;  // Clearly doesn't need validation.
   2110  int64_t intWrite0 = 0;
   2111  int64_t intSize = readSize;
   2112 
   2113  if (read1 <= 0 || read0 >= srcSize) {
   2114    // Disjoint ranges.
   2115    intSize = 0;
   2116  } else {
   2117    if (read0 < 0) {
   2118      const auto diff = int64_t(0) - read0;
   2119      MOZ_ASSERT(diff >= 0);
   2120      intRead0 = 0;
   2121      intWrite0 = diff;
   2122      intSize -= diff;
   2123    }
   2124    if (read1 > srcSize) {
   2125      const auto diff = int64_t(read1) - srcSize;
   2126      MOZ_ASSERT(diff >= 0);
   2127      intSize -= diff;
   2128    }
   2129 
   2130    if (!CheckedInt<int32_t>(intWrite0).isValid() ||
   2131        !CheckedInt<int32_t>(intSize).isValid()) {
   2132      return false;
   2133    }
   2134  }
   2135 
   2136  *out_intRead0 = intRead0;
   2137  *out_intWrite0 = intWrite0;
   2138  *out_intSize = intSize;
   2139  return true;
   2140 }
   2141 
   2142 // --
   2143 
   2144 uint64_t AvailGroups(const uint64_t totalAvailItems,
   2145                     const uint64_t firstItemOffset, const uint32_t groupSize,
   2146                     const uint32_t groupStride) {
   2147  MOZ_ASSERT(groupSize && groupStride);
   2148  MOZ_ASSERT(groupSize <= groupStride);
   2149 
   2150  if (totalAvailItems <= firstItemOffset) return 0;
   2151  const size_t availItems = totalAvailItems - firstItemOffset;
   2152 
   2153  size_t availGroups = availItems / groupStride;
   2154  const size_t tailItems = availItems % groupStride;
   2155  if (tailItems >= groupSize) {
   2156    availGroups += 1;
   2157  }
   2158  return availGroups;
   2159 }
   2160 
   2161 ////////////////////////////////////////////////////////////////////////////////
   2162 
   2163 const char* WebGLContext::FuncName() const {
   2164  const char* ret;
   2165  if (MOZ_LIKELY(mFuncScope)) {
   2166    ret = mFuncScope->mFuncName;
   2167  } else {
   2168    ret = "<unknown function>";
   2169  }
   2170  return ret;
   2171 }
   2172 
   2173 // -
   2174 
   2175 WebGLContext::FuncScope::FuncScope(const WebGLContext& webgl,
   2176                                   const char* const funcName)
   2177    : mWebGL(webgl), mFuncName(bool(mWebGL.mFuncScope) ? nullptr : funcName) {
   2178  if (!mFuncName) return;
   2179  mWebGL.mFuncScope = this;
   2180 }
   2181 
   2182 WebGLContext::FuncScope::~FuncScope() {
   2183  if (mBindFailureGuard) {
   2184    gfxCriticalError() << "mBindFailureGuard failure: Early exit from "
   2185                       << mWebGL.FuncName();
   2186  }
   2187 
   2188  if (!mFuncName) return;
   2189  mWebGL.mFuncScope = nullptr;
   2190 }
   2191 
   2192 // --
   2193 
   2194 bool ClientWebGLContext::IsXRCompatible() const { return mXRCompatible; }
   2195 
   2196 already_AddRefed<dom::Promise> ClientWebGLContext::MakeXRCompatible(
   2197    ErrorResult& aRv) {
   2198  const FuncScope funcScope(*this, "MakeXRCompatible");
   2199  nsCOMPtr<nsIGlobalObject> global = GetParentObject();
   2200  if (!global) {
   2201    aRv.ThrowInvalidAccessError(
   2202        "Using a WebGL context that is not attached to either a canvas or an "
   2203        "OffscreenCanvas");
   2204    return nullptr;
   2205  }
   2206  RefPtr<dom::Promise> promise = dom::Promise::Create(global, aRv);
   2207  NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
   2208 
   2209  if (IsContextLost()) {
   2210    promise->MaybeRejectWithInvalidStateError(
   2211        "Can not make context XR compatible when context is already lost.");
   2212    return promise.forget();
   2213  }
   2214 
   2215  // TODO: Bug 1580258 - WebGLContext.MakeXRCompatible needs to switch to
   2216  //                     the device connected to the XR hardware
   2217  // This should update `options` and lose+restore the context.
   2218  mXRCompatible = true;
   2219  promise->MaybeResolveWithUndefined();
   2220  return promise.forget();
   2221 }
   2222 
   2223 // --
   2224 
   2225 webgl::AvailabilityRunnable& ClientWebGLContext::EnsureAvailabilityRunnable()
   2226    const {
   2227  if (!mAvailabilityRunnable) {
   2228    mAvailabilityRunnable = new webgl::AvailabilityRunnable(this);
   2229    auto forgettable = mAvailabilityRunnable;
   2230    NS_DispatchToCurrentThread(forgettable.forget());
   2231  }
   2232  return *mAvailabilityRunnable;
   2233 }
   2234 
   2235 webgl::AvailabilityRunnable::AvailabilityRunnable(
   2236    const ClientWebGLContext* const webgl)
   2237    : DiscardableRunnable("webgl::AvailabilityRunnable"), mWebGL(webgl) {}
   2238 
   2239 webgl::AvailabilityRunnable::~AvailabilityRunnable() {
   2240  MOZ_ASSERT(mQueries.empty());
   2241  MOZ_ASSERT(mSyncs.empty());
   2242 }
   2243 
   2244 nsresult webgl::AvailabilityRunnable::Run() {
   2245  for (const auto& cur : mQueries) {
   2246    if (!cur) continue;
   2247    cur->mCanBeAvailable = true;
   2248  }
   2249  mQueries.clear();
   2250 
   2251  for (const auto& cur : mSyncs) {
   2252    if (!cur) continue;
   2253    cur->mCanBeAvailable = true;
   2254  }
   2255  mSyncs.clear();
   2256 
   2257  if (mWebGL) {
   2258    mWebGL->mAvailabilityRunnable = nullptr;
   2259  }
   2260  return NS_OK;
   2261 }
   2262 
   2263 // -
   2264 
   2265 void WebGLContext::JsWarning(const std::string& text) const {
   2266  if (mHost) {
   2267    mHost->JsWarning(text);
   2268  }
   2269 #ifdef DEBUG
   2270  if (!mHost) {
   2271    NS_WARNING(text.c_str());
   2272  }
   2273 #endif
   2274 }
   2275 
   2276 void WebGLContext::GenerateErrorImpl(const GLenum errOrWarning,
   2277                                     const std::string& text) const {
   2278  auto err = errOrWarning;
   2279  bool isPerfWarning = false;
   2280  if (err == webgl::kErrorPerfWarning) {
   2281    err = 0;
   2282    isPerfWarning = true;
   2283  }
   2284 
   2285  if (err && mFuncScope && mFuncScope->mBindFailureGuard) {
   2286    gfxCriticalError() << "mBindFailureGuard failure: Generating error "
   2287                       << EnumString(err) << ": " << text;
   2288  }
   2289 
   2290  /* ES2 section 2.5 "GL Errors" states that implementations can have
   2291   * multiple 'flags', as errors might be caught in different parts of
   2292   * a distributed implementation.
   2293   * We're signing up as a distributed implementation here, with
   2294   * separate flags for WebGL and the underlying GLContext.
   2295   */
   2296  if (!mWebGLError) mWebGLError = err;
   2297 
   2298  if (!mHost) return;  // Impossible?
   2299 
   2300  // -
   2301 
   2302  const auto ShouldWarn = [&]() {
   2303    if (isPerfWarning) {
   2304      return ShouldGeneratePerfWarnings();
   2305    }
   2306    return ShouldGenerateWarnings();
   2307  };
   2308  if (!ShouldWarn()) return;
   2309 
   2310  // -
   2311 
   2312  auto* pNumWarnings = &mWarningCount;
   2313  const char* warningsType = "warnings";
   2314  if (isPerfWarning) {
   2315    pNumWarnings = &mNumPerfWarnings;
   2316    warningsType = "perf warnings";
   2317  }
   2318 
   2319  if (isPerfWarning) {
   2320    const auto perfText = std::string("WebGL perf warning: ") + text;
   2321    JsWarning(perfText);
   2322  } else {
   2323    JsWarning(text);
   2324  }
   2325  *pNumWarnings += 1;
   2326 
   2327  if (!ShouldWarn()) {
   2328    const auto& msg = nsPrintfCString(
   2329        "After reporting %i, no further %s will be reported for this WebGL "
   2330        "context.",
   2331        int(*pNumWarnings), warningsType);
   2332    JsWarning(ToString(msg));
   2333  }
   2334 }
   2335 
   2336 // -
   2337 
   2338 Maybe<std::string> WebGLContext::GetString(const GLenum pname) const {
   2339  const WebGLContext::FuncScope funcScope(*this, "getParameter");
   2340  if (IsContextLost()) return {};
   2341 
   2342  const auto FromRaw = [](const char* const raw) -> Maybe<std::string> {
   2343    if (!raw) return {};
   2344    return Some(std::string(raw));
   2345  };
   2346 
   2347  switch (pname) {
   2348    case LOCAL_GL_EXTENSIONS: {
   2349      if (!gl->IsCoreProfile()) {
   2350        const auto rawExt = (const char*)gl->fGetString(LOCAL_GL_EXTENSIONS);
   2351        return FromRaw(rawExt);
   2352      }
   2353      std::string ret;
   2354      const auto& numExts = gl->GetIntAs<GLuint>(LOCAL_GL_NUM_EXTENSIONS);
   2355      for (GLuint i = 0; i < numExts; i++) {
   2356        const auto rawExt =
   2357            (const char*)gl->fGetStringi(LOCAL_GL_EXTENSIONS, i);
   2358        if (!rawExt) continue;
   2359 
   2360        if (i > 0) {
   2361          ret += " ";
   2362        }
   2363        ret += rawExt;
   2364      }
   2365      return Some(std::move(ret));
   2366    }
   2367 
   2368    case LOCAL_GL_RENDERER:
   2369    case LOCAL_GL_VENDOR:
   2370    case LOCAL_GL_VERSION: {
   2371      const auto raw = (const char*)gl->fGetString(pname);
   2372      return FromRaw(raw);
   2373    }
   2374 
   2375    case dom::MOZ_debug_Binding::WSI_INFO: {
   2376      nsCString info;
   2377      gl->GetWSIInfo(&info);
   2378      return Some(std::string(info.BeginReading()));
   2379    }
   2380 
   2381    case dom::MOZ_debug_Binding::CONTEXT_TYPE: {
   2382      gl::GLContextType ctxType = gl->GetContextType();
   2383      switch (ctxType) {
   2384        case gl::GLContextType::Unknown:
   2385          return Some("unknown"_ns);
   2386        case gl::GLContextType::WGL:
   2387          return Some("wgl"_ns);
   2388        case gl::GLContextType::CGL:
   2389          return Some("cgl"_ns);
   2390        case gl::GLContextType::GLX:
   2391          return Some("glx"_ns);
   2392        case gl::GLContextType::EGL:
   2393          return Some("egl"_ns);
   2394        case gl::GLContextType::EAGL:
   2395          return Some("eagl"_ns);
   2396      }
   2397      return Some("unknown"_ns);
   2398    }
   2399 
   2400    default:
   2401      ErrorInvalidEnumArg("pname", pname);
   2402      return {};
   2403  }
   2404 }
   2405 
   2406 // ---------------------------------
   2407 
   2408 Maybe<webgl::IndexedName> webgl::ParseIndexed(const std::string& str) {
   2409  // Check if the string ends with a close bracket
   2410  if (str.size() < 2 || str.back() != ']') {
   2411    return {};
   2412  }
   2413  // Search for the open bracket, only allow digits between brackets
   2414  const size_t closeBracket = str.size() - 1;
   2415  size_t openBracket = closeBracket;
   2416  for (;;) {
   2417    char c = str[--openBracket];
   2418    if (isdigit(c)) {
   2419      if (openBracket <= 0) {
   2420        // At the beginning of string without an open bracket
   2421        return {};
   2422      }
   2423    } else if (c == '[') {
   2424      // Found the open bracket
   2425      break;
   2426    } else {
   2427      // Found a non-digit
   2428      return {};
   2429    }
   2430  }
   2431 
   2432  // Ensure non-empty digit sequence
   2433  size_t firstDigit = openBracket + 1;
   2434  if (firstDigit >= closeBracket) {
   2435    return {};
   2436  }
   2437  const auto index =
   2438      std::stoull(str.substr(firstDigit, closeBracket - firstDigit));
   2439  std::string name = str.substr(0, openBracket);
   2440  return Some(webgl::IndexedName{name, index});
   2441 }
   2442 
   2443 // ExplodeName("foo.bar[3].x") -> ["foo", ".", "bar", "[", "3", "]", ".", "x"]
   2444 static std::vector<std::string> ExplodeName(const std::string& str) {
   2445  std::vector<std::string> ret;
   2446  size_t curPos = 0;
   2447  while (curPos < str.size()) {
   2448    // Find the next separator
   2449    size_t nextPos = str.find_first_of(".[]", curPos);
   2450    if (nextPos == std::string::npos) {
   2451      // If no separator found, add remaining substring
   2452      ret.push_back(str.substr(curPos));
   2453      break;
   2454    }
   2455    // Add string between separators, if not empty
   2456    if (curPos < nextPos) {
   2457      ret.push_back(str.substr(curPos, nextPos - curPos));
   2458    }
   2459    // Add the separator
   2460    ret.push_back(str.substr(nextPos, 1));
   2461    curPos = nextPos + 1;
   2462  }
   2463  return ret;
   2464 }
   2465 
   2466 //-
   2467 
   2468 // #define DUMP_MakeLinkResult
   2469 
   2470 webgl::LinkActiveInfo GetLinkActiveInfo(
   2471    gl::GLContext& gl, const GLuint prog, const bool webgl2,
   2472    const std::unordered_map<std::string, std::string>& nameUnmap) {
   2473  webgl::LinkActiveInfo ret;
   2474  [&]() {
   2475    const auto fnGetProgramui = [&](const GLenum pname) {
   2476      GLint ret = 0;
   2477      gl.fGetProgramiv(prog, pname, &ret);
   2478      return static_cast<uint32_t>(ret);
   2479    };
   2480 
   2481    std::vector<char> stringBuffer(1);
   2482    const auto fnEnsureCapacity = [&](const GLenum pname) {
   2483      const auto maxWithNull = fnGetProgramui(pname);
   2484      if (maxWithNull > stringBuffer.size()) {
   2485        stringBuffer.resize(maxWithNull);
   2486      }
   2487    };
   2488 
   2489    fnEnsureCapacity(LOCAL_GL_ACTIVE_ATTRIBUTE_MAX_LENGTH);
   2490    fnEnsureCapacity(LOCAL_GL_ACTIVE_UNIFORM_MAX_LENGTH);
   2491    if (webgl2) {
   2492      fnEnsureCapacity(LOCAL_GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH);
   2493      fnEnsureCapacity(LOCAL_GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH);
   2494    }
   2495 
   2496    // -
   2497 
   2498    const auto fnUnmapName = [&](const std::string& mappedName) {
   2499      const auto parts = ExplodeName(mappedName);
   2500 
   2501      std::ostringstream ret;
   2502      for (const auto& part : parts) {
   2503        const auto maybe = MaybeFind(nameUnmap, part);
   2504        if (maybe) {
   2505          ret << *maybe;
   2506        } else {
   2507          ret << part;
   2508        }
   2509      }
   2510      return ret.str();
   2511    };
   2512 
   2513    // -
   2514 
   2515    {
   2516      const auto count = fnGetProgramui(LOCAL_GL_ACTIVE_ATTRIBUTES);
   2517      ret.activeAttribs.reserve(count);
   2518      for (const auto i : IntegerRange(count)) {
   2519        GLsizei lengthWithoutNull = 0;
   2520        GLint elemCount = 0;  // `size`
   2521        GLenum elemType = 0;  // `type`
   2522        gl.fGetActiveAttrib(prog, i, stringBuffer.size(), &lengthWithoutNull,
   2523                            &elemCount, &elemType, stringBuffer.data());
   2524        if (!elemType) {
   2525          const auto error = gl.fGetError();
   2526          if (error != LOCAL_GL_CONTEXT_LOST) {
   2527            gfxCriticalError() << "Failed to do glGetActiveAttrib: " << error;
   2528          }
   2529          return;
   2530        }
   2531        const auto mappedName =
   2532            std::string(stringBuffer.data(), lengthWithoutNull);
   2533        const auto userName = fnUnmapName(mappedName);
   2534 
   2535        auto loc = gl.fGetAttribLocation(prog, mappedName.c_str());
   2536        if (mappedName.find("gl_") == 0) {
   2537          // Bug 1328559: Appears problematic on ANGLE and OSX, but not Linux or
   2538          // Win+GL.
   2539          loc = -1;
   2540        }
   2541 
   2542 #ifdef DUMP_MakeLinkResult
   2543        printf_stderr("[attrib %u/%u] @%i %s->%s\n", i, count, loc,
   2544                      userName.c_str(), mappedName.c_str());
   2545 #endif
   2546        webgl::ActiveAttribInfo info;
   2547        info.elemType = elemType;
   2548        info.elemCount = elemCount;
   2549        info.name = userName;
   2550        info.location = loc;
   2551        info.baseType = webgl::ToAttribBaseType(info.elemType);
   2552        ret.activeAttribs.push_back(std::move(info));
   2553      }
   2554    }
   2555 
   2556    // -
   2557 
   2558    {
   2559      const auto count = fnGetProgramui(LOCAL_GL_ACTIVE_UNIFORMS);
   2560      ret.activeUniforms.reserve(count);
   2561 
   2562      std::vector<GLint> blockIndexList(count, -1);
   2563      std::vector<GLint> blockOffsetList(count, -1);
   2564      std::vector<GLint> blockArrayStrideList(count, -1);
   2565      std::vector<GLint> blockMatrixStrideList(count, -1);
   2566      std::vector<GLint> blockIsRowMajorList(count, 0);
   2567 
   2568      if (webgl2 && count) {
   2569        std::vector<GLuint> activeIndices;
   2570        activeIndices.reserve(count);
   2571        for (const auto i : IntegerRange(count)) {
   2572          activeIndices.push_back(i);
   2573        }
   2574 
   2575        gl.fGetActiveUniformsiv(
   2576            prog, activeIndices.size(), activeIndices.data(),
   2577            LOCAL_GL_UNIFORM_BLOCK_INDEX, blockIndexList.data());
   2578 
   2579        gl.fGetActiveUniformsiv(prog, activeIndices.size(),
   2580                                activeIndices.data(), LOCAL_GL_UNIFORM_OFFSET,
   2581                                blockOffsetList.data());
   2582 
   2583        gl.fGetActiveUniformsiv(
   2584            prog, activeIndices.size(), activeIndices.data(),
   2585            LOCAL_GL_UNIFORM_ARRAY_STRIDE, blockArrayStrideList.data());
   2586 
   2587        gl.fGetActiveUniformsiv(
   2588            prog, activeIndices.size(), activeIndices.data(),
   2589            LOCAL_GL_UNIFORM_MATRIX_STRIDE, blockMatrixStrideList.data());
   2590 
   2591        gl.fGetActiveUniformsiv(
   2592            prog, activeIndices.size(), activeIndices.data(),
   2593            LOCAL_GL_UNIFORM_IS_ROW_MAJOR, blockIsRowMajorList.data());
   2594      }
   2595 
   2596      for (const auto i : IntegerRange(count)) {
   2597        GLsizei lengthWithoutNull = 0;
   2598        GLint elemCount = 0;  // `size`
   2599        GLenum elemType = 0;  // `type`
   2600        gl.fGetActiveUniform(prog, i, stringBuffer.size(), &lengthWithoutNull,
   2601                             &elemCount, &elemType, stringBuffer.data());
   2602        if (!elemType) {
   2603          const auto error = gl.fGetError();
   2604          if (error != LOCAL_GL_CONTEXT_LOST) {
   2605            gfxCriticalError() << "Failed to do glGetActiveUniform: " << error;
   2606          }
   2607          return;
   2608        }
   2609        auto mappedName = std::string(stringBuffer.data(), lengthWithoutNull);
   2610 
   2611        // Get true name
   2612 
   2613        auto baseMappedName = mappedName;
   2614 
   2615        const bool isArray = [&]() {
   2616          const auto maybe = webgl::ParseIndexed(mappedName);
   2617          if (maybe) {
   2618            MOZ_ASSERT(maybe->index == 0);
   2619            baseMappedName = std::move(maybe->name);
   2620            return true;
   2621          }
   2622          return false;
   2623        }();
   2624 
   2625        const auto userName = fnUnmapName(mappedName);
   2626        if (StartsWith(userName, "webgl_")) continue;
   2627 
   2628        // -
   2629 
   2630        webgl::ActiveUniformInfo info;
   2631        info.elemType = elemType;
   2632        info.elemCount = static_cast<uint32_t>(elemCount);
   2633        info.name = userName;
   2634        info.block_index = blockIndexList[i];
   2635        info.block_offset = blockOffsetList[i];
   2636        info.block_arrayStride = blockArrayStrideList[i];
   2637        info.block_matrixStride = blockMatrixStrideList[i];
   2638        info.block_isRowMajor = bool(blockIsRowMajorList[i]);
   2639 
   2640 #ifdef DUMP_MakeLinkResult
   2641        printf_stderr("[uniform %u/%u] %s->%s\n", i + 1, count,
   2642                      userName.c_str(), mappedName.c_str());
   2643 #endif
   2644 
   2645        // Get uniform locations
   2646        {
   2647          auto locName = baseMappedName;
   2648          const auto baseLength = locName.size();
   2649          for (const auto i : IntegerRange(info.elemCount)) {
   2650            if (isArray) {
   2651              locName.erase(
   2652                  baseLength);  // Erase previous [N], but retain capacity.
   2653              locName += '[';
   2654              locName += std::to_string(i);
   2655              locName += ']';
   2656            }
   2657            const auto loc = gl.fGetUniformLocation(prog, locName.c_str());
   2658            if (loc != -1) {
   2659              info.locByIndex[i] = static_cast<uint32_t>(loc);
   2660 #ifdef DUMP_MakeLinkResult
   2661              printf_stderr("   [%u] @%i\n", i, loc);
   2662 #endif
   2663            }
   2664          }
   2665        }  // anon
   2666 
   2667        ret.activeUniforms.push_back(std::move(info));
   2668      }  // for i
   2669    }  // anon
   2670 
   2671    if (webgl2) {
   2672      // -------------------------------------
   2673      // active uniform blocks
   2674      {
   2675        const auto count = fnGetProgramui(LOCAL_GL_ACTIVE_UNIFORM_BLOCKS);
   2676        ret.activeUniformBlocks.reserve(count);
   2677 
   2678        for (const auto i : IntegerRange(count)) {
   2679          GLsizei lengthWithoutNull = 0;
   2680          gl.fGetActiveUniformBlockName(prog, i, stringBuffer.size(),
   2681                                        &lengthWithoutNull,
   2682                                        stringBuffer.data());
   2683          const auto mappedName =
   2684              std::string(stringBuffer.data(), lengthWithoutNull);
   2685          const auto userName = fnUnmapName(mappedName);
   2686 
   2687          // -
   2688 
   2689          auto info = webgl::ActiveUniformBlockInfo{userName};
   2690          GLint val = 0;
   2691 
   2692          gl.fGetActiveUniformBlockiv(prog, i, LOCAL_GL_UNIFORM_BLOCK_DATA_SIZE,
   2693                                      &val);
   2694          info.dataSize = static_cast<uint32_t>(val);
   2695 
   2696          gl.fGetActiveUniformBlockiv(
   2697              prog, i, LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &val);
   2698          info.activeUniformIndices.resize(val);
   2699          gl.fGetActiveUniformBlockiv(
   2700              prog, i, LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES,
   2701              reinterpret_cast<GLint*>(info.activeUniformIndices.data()));
   2702 
   2703          gl.fGetActiveUniformBlockiv(
   2704              prog, i, LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER,
   2705              &val);
   2706          info.referencedByVertexShader = bool(val);
   2707 
   2708          gl.fGetActiveUniformBlockiv(
   2709              prog, i, LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER,
   2710              &val);
   2711          info.referencedByFragmentShader = bool(val);
   2712 
   2713          ret.activeUniformBlocks.push_back(std::move(info));
   2714        }  // for i
   2715      }  // anon
   2716 
   2717      // -------------------------------------
   2718      // active tf varyings
   2719      {
   2720        const auto count = fnGetProgramui(LOCAL_GL_TRANSFORM_FEEDBACK_VARYINGS);
   2721        ret.activeTfVaryings.reserve(count);
   2722 
   2723        for (const auto i : IntegerRange(count)) {
   2724          GLsizei lengthWithoutNull = 0;
   2725          GLsizei elemCount = 0;  // `size`
   2726          GLenum elemType = 0;    // `type`
   2727          gl.fGetTransformFeedbackVarying(prog, i, stringBuffer.size(),
   2728                                          &lengthWithoutNull, &elemCount,
   2729                                          &elemType, stringBuffer.data());
   2730          const auto mappedName =
   2731              std::string(stringBuffer.data(), lengthWithoutNull);
   2732          const auto userName = fnUnmapName(mappedName);
   2733 
   2734          ret.activeTfVaryings.push_back(
   2735              {elemType, static_cast<uint32_t>(elemCount), userName});
   2736        }
   2737      }
   2738    }  // if webgl2
   2739  }();
   2740  return ret;
   2741 }
   2742 
   2743 nsCString ToCString(const std::string& s) {
   2744  return nsCString(s.data(), s.size());
   2745 }
   2746 
   2747 webgl::CompileResult WebGLContext::GetCompileResult(
   2748    const WebGLShader& shader) const {
   2749  webgl::CompileResult ret;
   2750  [&]() {
   2751    ret.pending = false;
   2752    const auto& info = shader.CompileResults();
   2753    if (!info) return;
   2754    if (!info->mValid) {
   2755      ret.log = info->mInfoLog.c_str();
   2756      return;
   2757    }
   2758    // TODO: These could be large and should be made fallible.
   2759    ret.translatedSource = ToCString(info->mObjectCode);
   2760    ret.log = ToCString(shader.CompileLog());
   2761    if (!shader.IsCompiled()) return;
   2762    ret.success = true;
   2763  }();
   2764  return ret;
   2765 }
   2766 
   2767 webgl::LinkResult WebGLContext::GetLinkResult(const WebGLProgram& prog) const {
   2768  webgl::LinkResult ret;
   2769  [&]() {
   2770    ret.pending = false;  // Link status polling not yet implemented.
   2771    ret.log = ToCString(prog.LinkLog());
   2772    const auto& info = prog.LinkInfo();
   2773    if (!info) return;
   2774    ret.success = true;
   2775    ret.active = info->active;
   2776    ret.tfBufferMode = info->transformFeedbackBufferMode;
   2777  }();
   2778  return ret;
   2779 }
   2780 
   2781 // -
   2782 
   2783 GLint WebGLContext::GetFragDataLocation(const WebGLProgram& prog,
   2784                                        const std::string& userName) const {
   2785  const auto err = CheckGLSLVariableName(IsWebGL2(), userName);
   2786  if (err) {
   2787    GenerateError(err->type, "%s", err->info.c_str());
   2788    return -1;
   2789  }
   2790 
   2791  const auto& info = prog.LinkInfo();
   2792  if (!info) return -1;
   2793  const auto& nameMap = info->nameMap;
   2794 
   2795  const auto parts = ExplodeName(userName);
   2796 
   2797  std::ostringstream ret;
   2798  for (const auto& part : parts) {
   2799    const auto maybe = MaybeFind(nameMap, part);
   2800    if (maybe) {
   2801      ret << *maybe;
   2802    } else {
   2803      ret << part;
   2804    }
   2805  }
   2806  const auto mappedName = ret.str();
   2807 
   2808  if (gl->WorkAroundDriverBugs() && gl->IsMesa()) {
   2809    // Mesa incorrectly generates INVALID_OPERATION for gl_ prefixes here.
   2810    if (mappedName.find("gl_") == 0) {
   2811      return -1;
   2812    }
   2813  }
   2814 
   2815  return gl->fGetFragDataLocation(prog.mGLName, mappedName.c_str());
   2816 }
   2817 
   2818 // -
   2819 
   2820 WebGLContextBoundObject::WebGLContextBoundObject(WebGLContext* webgl)
   2821    : mContext(webgl) {}
   2822 
   2823 // -
   2824 
   2825 Result<webgl::ExplicitPixelPackingState, std::string>
   2826 webgl::ExplicitPixelPackingState::ForUseWith(
   2827    const webgl::PixelPackingState& stateOrZero, const GLenum target,
   2828    const uvec3& subrectSize, const webgl::PackingInfo& pi,
   2829    const Maybe<size_t> bytesPerRowStrideOverride) {
   2830  auto state = stateOrZero;
   2831 
   2832  if (!IsTexTarget3D(target)) {
   2833    state.skipImages = 0;
   2834    state.imageHeight = 0;
   2835  }
   2836  if (!state.rowLength) {
   2837    state.rowLength = subrectSize.x;
   2838  }
   2839  if (!state.imageHeight) {
   2840    state.imageHeight = subrectSize.y;
   2841  }
   2842 
   2843  // -
   2844 
   2845  const auto mpii = PackingInfoInfo::For(pi);
   2846  if (!mpii) {
   2847    const auto text =
   2848        nsPrintfCString("Invalid pi: { 0x%x, 0x%x}", pi.format, pi.type);
   2849    return Err(mozilla::ToString(text));
   2850  }
   2851  const auto pii = *mpii;
   2852  const auto bytesPerPixel = pii.BytesPerPixel();
   2853 
   2854  const auto ElemsPerRowStride = [&]() {
   2855    // GLES 3.0.6 p116:
   2856    // p: `Elem*` pointer to the first element of the first row
   2857    // N: row number, starting at 0
   2858    // l: groups (pixels) per row
   2859    // n: elements per group (pixel) in [1,2,3,4]
   2860    // s: bytes per element in [1,2,4,8]
   2861    // a: UNPACK_ALIGNMENT in [1,2,4,8]
   2862    // Pointer to first element of Nth row: p + N*k
   2863    // k(s>=a): n*l
   2864    // k(s<a): a/s * ceil(s*n*l/a)
   2865    const auto n__elemsPerPixel = pii.elementsPerPixel;
   2866    const auto l__pixelsPerRow = state.rowLength;
   2867    const auto a__alignment = state.alignmentInTypeElems;
   2868    const auto s__bytesPerElem = pii.bytesPerElement;
   2869 
   2870    const auto nl = CheckedInt<size_t>(n__elemsPerPixel) * l__pixelsPerRow;
   2871    auto k__elemsPerRowStride = nl;
   2872    if (s__bytesPerElem < a__alignment) {
   2873      // k = a/s * ceil(s*n*l/a)
   2874      k__elemsPerRowStride =
   2875          a__alignment / s__bytesPerElem *
   2876          ((nl * s__bytesPerElem + a__alignment - 1) / a__alignment);
   2877    }
   2878    return k__elemsPerRowStride;
   2879  };
   2880 
   2881  // -
   2882 
   2883  if (bytesPerRowStrideOverride) {  // E.g. HTMLImageElement
   2884    const size_t bytesPerRowStrideRequired = *bytesPerRowStrideOverride;
   2885    // We have to reverse-engineer an ALIGNMENT and ROW_LENGTH for this.
   2886 
   2887    // GL does this in elems not bytes, so we should too.
   2888    MOZ_RELEASE_ASSERT(bytesPerRowStrideRequired % pii.bytesPerElement == 0);
   2889    const auto elemsPerRowStrideRequired =
   2890        bytesPerRowStrideRequired / pii.bytesPerElement;
   2891 
   2892    state.rowLength = bytesPerRowStrideRequired / bytesPerPixel;
   2893    state.alignmentInTypeElems = 8;
   2894    while (true) {
   2895      const auto elemPerRowStride = ElemsPerRowStride();
   2896      if (elemPerRowStride.isValid() &&
   2897          elemPerRowStride.value() == elemsPerRowStrideRequired) {
   2898        break;
   2899      }
   2900      state.alignmentInTypeElems /= 2;
   2901      if (!state.alignmentInTypeElems) {
   2902        const auto text = nsPrintfCString(
   2903            "No valid alignment found: pi: { 0x%x, 0x%x},"
   2904            " bytesPerRowStrideRequired: %zu",
   2905            pi.format, pi.type, bytesPerRowStrideRequired);
   2906        return Err(mozilla::ToString(text));
   2907      }
   2908    }
   2909  }
   2910 
   2911  // -
   2912 
   2913  const auto usedPixelsPerRow =
   2914      CheckedInt<size_t>(state.skipPixels) + subrectSize.x;
   2915  if (!usedPixelsPerRow.isValid() ||
   2916      usedPixelsPerRow.value() > state.rowLength) {
   2917    return Err("UNPACK_SKIP_PIXELS + width > UNPACK_ROW_LENGTH.");
   2918  }
   2919 
   2920  if (subrectSize.y > state.imageHeight) {
   2921    return Err("height > UNPACK_IMAGE_HEIGHT.");
   2922  }
   2923  // The spec doesn't bound SKIP_ROWS + height <= IMAGE_HEIGHT, unfortunately.
   2924 
   2925  // -
   2926 
   2927  auto metrics = Metrics{};
   2928 
   2929  metrics.usedSize = subrectSize;
   2930  metrics.bytesPerPixel = BytesPerPixel(pi);
   2931 
   2932  // -
   2933 
   2934  const auto elemsPerRowStride = ElemsPerRowStride();
   2935  const auto bytesPerRowStride = pii.bytesPerElement * elemsPerRowStride;
   2936  if (!bytesPerRowStride.isValid()) {
   2937    return Err("ROW_LENGTH or width too large for packing.");
   2938  }
   2939  metrics.bytesPerRowStride = bytesPerRowStride.value();
   2940 
   2941  // -
   2942 
   2943  const auto firstImageTotalRows =
   2944      CheckedInt<size_t>(state.skipRows) + metrics.usedSize.y;
   2945  const auto totalImages =
   2946      CheckedInt<size_t>(state.skipImages) + metrics.usedSize.z;
   2947  auto totalRows = CheckedInt<size_t>(0);
   2948  if (metrics.usedSize.y && metrics.usedSize.z) {
   2949    totalRows = firstImageTotalRows + state.imageHeight * (totalImages - 1);
   2950  }
   2951  if (!totalRows.isValid()) {
   2952    return Err(
   2953        "SKIP_ROWS, height, IMAGE_HEIGHT, SKIP_IMAGES, or depth too large for "
   2954        "packing.");
   2955  }
   2956  metrics.totalRows = totalRows.value();
   2957 
   2958  // -
   2959 
   2960  const auto totalBytesStrided = totalRows * metrics.bytesPerRowStride;
   2961  if (!totalBytesStrided.isValid()) {
   2962    return Err("Total byte count too large for packing.");
   2963  }
   2964  metrics.totalBytesStrided = totalBytesStrided.value();
   2965 
   2966  metrics.totalBytesUsed = metrics.totalBytesStrided;
   2967  if (metrics.usedSize.x && metrics.usedSize.y && metrics.usedSize.z) {
   2968    const auto usedBytesPerRow =
   2969        usedPixelsPerRow.value() * metrics.bytesPerPixel;
   2970    metrics.totalBytesUsed -= metrics.bytesPerRowStride;
   2971    metrics.totalBytesUsed += usedBytesPerRow;
   2972  }
   2973 
   2974  // -
   2975 
   2976  return {{state, metrics}};
   2977 }
   2978 
   2979 GLuint WebGLContext::SamplerLinear() const {
   2980  if (!mSamplerLinear) {
   2981    mSamplerLinear = std::make_unique<gl::Sampler>(*gl);
   2982    gl->fSamplerParameteri(mSamplerLinear->name, LOCAL_GL_TEXTURE_MAG_FILTER,
   2983                           LOCAL_GL_LINEAR);
   2984    gl->fSamplerParameteri(mSamplerLinear->name, LOCAL_GL_TEXTURE_MIN_FILTER,
   2985                           LOCAL_GL_LINEAR);
   2986    gl->fSamplerParameteri(mSamplerLinear->name, LOCAL_GL_TEXTURE_WRAP_S,
   2987                           LOCAL_GL_CLAMP_TO_EDGE);
   2988    gl->fSamplerParameteri(mSamplerLinear->name, LOCAL_GL_TEXTURE_WRAP_T,
   2989                           LOCAL_GL_CLAMP_TO_EDGE);
   2990    gl->fSamplerParameteri(mSamplerLinear->name, LOCAL_GL_TEXTURE_WRAP_R,
   2991                           LOCAL_GL_CLAMP_TO_EDGE);
   2992  }
   2993  return mSamplerLinear->name;
   2994 }
   2995 
   2996 }  // namespace mozilla