Bug 1700051: part 26) Correct typo in comment of `mozInlineSpellWordUtil::BuildSoftTe...
[gecko.git] / dom / canvas / WebGLContext.cpp
blob51f96c7b454e7fda35840cd2f9d502264428b436
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "WebGLContext.h"
8 #include <algorithm>
9 #include <queue>
10 #include <regex>
12 #include "AccessCheck.h"
13 #include "CompositableHost.h"
14 #include "gfxConfig.h"
15 #include "gfxContext.h"
16 #include "gfxCrashReporterUtils.h"
17 #include "gfxEnv.h"
18 #include "gfxPattern.h"
19 #include "gfxUtils.h"
20 #include "MozFramebuffer.h"
21 #include "GLBlitHelper.h"
22 #include "GLContext.h"
23 #include "GLContextProvider.h"
24 #include "GLReadTexImageHelper.h"
25 #include "GLScreenBuffer.h"
26 #include "ImageContainer.h"
27 #include "ImageEncoder.h"
28 #include "Layers.h"
29 #include "LayerUserData.h"
30 #include "mozilla/dom/BindingUtils.h"
31 #include "mozilla/dom/Document.h"
32 #include "mozilla/dom/Event.h"
33 #include "mozilla/dom/HTMLVideoElement.h"
34 #include "mozilla/dom/ImageData.h"
35 #include "mozilla/dom/WebGLContextEvent.h"
36 #include "mozilla/EnumeratedArrayCycleCollection.h"
37 #include "mozilla/EnumeratedRange.h"
38 #include "mozilla/gfx/gfxVars.h"
39 #include "mozilla/Preferences.h"
40 #include "mozilla/ProcessPriorityManager.h"
41 #include "mozilla/ScopeExit.h"
42 #include "mozilla/Services.h"
43 #include "mozilla/StaticPrefs_webgl.h"
44 #include "mozilla/SVGObserverUtils.h"
45 #include "mozilla/Telemetry.h"
46 #include "nsContentUtils.h"
47 #include "nsDisplayList.h"
48 #include "nsError.h"
49 #include "nsIClassInfoImpl.h"
50 #include "nsIWidget.h"
51 #include "nsServiceManagerUtils.h"
52 #include "SharedSurfaceGL.h"
53 #include "prenv.h"
54 #include "ScopedGLHelpers.h"
55 #include "VRManagerChild.h"
56 #include "mozilla/layers/CompositorBridgeChild.h"
57 #include "mozilla/layers/ImageBridgeChild.h"
58 #include "mozilla/layers/TextureClientSharedSurface.h"
59 #include "mozilla/layers/WebRenderUserData.h"
60 #include "mozilla/layers/WebRenderCanvasRenderer.h"
62 // Local
63 #include "CanvasUtils.h"
64 #include "ClientWebGLContext.h"
65 #include "HostWebGLContext.h"
66 #include "WebGLBuffer.h"
67 #include "WebGLChild.h"
68 #include "WebGLContextLossHandler.h"
69 #include "WebGLContextUtils.h"
70 #include "WebGLExtensions.h"
71 #include "WebGLFormats.h"
72 #include "WebGLFramebuffer.h"
73 #include "WebGLMemoryTracker.h"
74 #include "WebGLObjectModel.h"
75 #include "WebGLProgram.h"
76 #include "WebGLQuery.h"
77 #include "WebGLSampler.h"
78 #include "WebGLShader.h"
79 #include "WebGLShaderValidator.h"
80 #include "WebGLSync.h"
81 #include "WebGLTransformFeedback.h"
82 #include "WebGLValidateStrings.h"
83 #include "WebGLVertexArray.h"
85 #ifdef MOZ_WIDGET_COCOA
86 # include "nsCocoaFeatures.h"
87 #endif
89 #ifdef XP_WIN
90 # include "WGLLibrary.h"
91 #endif
93 // Generated
94 #include "mozilla/dom/WebGLRenderingContextBinding.h"
96 namespace mozilla {
98 WebGLContextOptions::WebGLContextOptions() {
99 // Set default alpha state based on preference.
100 alpha = !StaticPrefs::webgl_default_no_alpha();
101 antialias = StaticPrefs::webgl_default_antialias();
104 bool WebGLContextOptions::operator==(const WebGLContextOptions& r) const {
105 bool eq = true;
106 eq &= (alpha == r.alpha);
107 eq &= (depth == r.depth);
108 eq &= (stencil == r.stencil);
109 eq &= (premultipliedAlpha == r.premultipliedAlpha);
110 eq &= (antialias == r.antialias);
111 eq &= (preserveDrawingBuffer == r.preserveDrawingBuffer);
112 eq &= (failIfMajorPerformanceCaveat == r.failIfMajorPerformanceCaveat);
113 eq &= (xrCompatible == r.xrCompatible);
114 eq &= (powerPreference == r.powerPreference);
115 return eq;
118 static std::list<WebGLContext*> sWebglLru;
120 WebGLContext::LruPosition::LruPosition() : mItr(sWebglLru.end()) {} // NOLINT
122 WebGLContext::LruPosition::LruPosition(WebGLContext& context)
123 : mItr(sWebglLru.insert(sWebglLru.end(), &context)) {}
125 void WebGLContext::LruPosition::reset() {
126 const auto end = sWebglLru.end();
127 if (mItr != end) {
128 sWebglLru.erase(mItr);
129 mItr = end;
133 WebGLContext::WebGLContext(HostWebGLContext& host,
134 const webgl::InitContextDesc& desc)
135 : gl(mGL_OnlyClearInDestroyResourcesAndContext), // const reference
136 mHost(&host),
137 mResistFingerprinting(desc.resistFingerprinting),
138 mOptions(desc.options),
139 mPrincipalKey(desc.principalKey),
140 mMaxPerfWarnings(StaticPrefs::webgl_perf_max_warnings()),
141 mMaxAcceptableFBStatusInvals(
142 StaticPrefs::webgl_perf_max_acceptable_fb_status_invals()),
143 mContextLossHandler(this),
144 mMaxWarnings(StaticPrefs::webgl_max_warnings_per_context()),
145 mAllowFBInvalidation(StaticPrefs::webgl_allow_fb_invalidation()),
146 mMsaaSamples((uint8_t)StaticPrefs::webgl_msaa_samples()),
147 mRequestedSize(desc.size) {
148 host.mContext = this;
149 const FuncScope funcScope(*this, "<Create>");
152 WebGLContext::~WebGLContext() { DestroyResourcesAndContext(); }
154 void WebGLContext::DestroyResourcesAndContext() {
155 if (!gl) return;
157 mDefaultFB = nullptr;
158 mResolvedDefaultFB = nullptr;
160 mBound2DTextures.Clear();
161 mBoundCubeMapTextures.Clear();
162 mBound3DTextures.Clear();
163 mBound2DArrayTextures.Clear();
164 mBoundSamplers.Clear();
165 mBoundArrayBuffer = nullptr;
166 mBoundCopyReadBuffer = nullptr;
167 mBoundCopyWriteBuffer = nullptr;
168 mBoundPixelPackBuffer = nullptr;
169 mBoundPixelUnpackBuffer = nullptr;
170 mBoundTransformFeedbackBuffer = nullptr;
171 mBoundUniformBuffer = nullptr;
172 mCurrentProgram = nullptr;
173 mActiveProgramLinkInfo = nullptr;
174 mBoundDrawFramebuffer = nullptr;
175 mBoundReadFramebuffer = nullptr;
176 mBoundVertexArray = nullptr;
177 mDefaultVertexArray = nullptr;
178 mBoundTransformFeedback = nullptr;
179 mDefaultTransformFeedback = nullptr;
181 mQuerySlot_SamplesPassed = nullptr;
182 mQuerySlot_TFPrimsWritten = nullptr;
183 mQuerySlot_TimeElapsed = nullptr;
185 mIndexedUniformBufferBindings.clear();
187 //////
189 if (mEmptyTFO) {
190 gl->fDeleteTransformFeedbacks(1, &mEmptyTFO);
191 mEmptyTFO = 0;
194 //////
196 if (mFakeVertexAttrib0BufferObject) {
197 gl->fDeleteBuffers(1, &mFakeVertexAttrib0BufferObject);
198 mFakeVertexAttrib0BufferObject = 0;
201 // disable all extensions except "WEBGL_lose_context". see bug #927969
202 // spec: http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
203 for (size_t i = 0; i < size_t(WebGLExtensionID::Max); ++i) {
204 WebGLExtensionID extension = WebGLExtensionID(i);
205 if (extension == WebGLExtensionID::WEBGL_lose_context) continue;
206 mExtensions[extension] = nullptr;
209 // We just got rid of everything, so the context had better
210 // have been going away.
211 if (gl::GLContext::ShouldSpew()) {
212 printf_stderr("--- WebGL context destroyed: %p\n", gl.get());
215 MOZ_ASSERT(gl);
216 gl->MarkDestroyed();
217 mGL_OnlyClearInDestroyResourcesAndContext = nullptr;
218 MOZ_ASSERT(!gl);
221 void ClientWebGLContext::MarkCanvasDirty() {
222 if (!mCanvasElement) return;
224 mCapturedFrameInvalidated = true;
226 if (mIsCanvasDirty) return;
227 mIsCanvasDirty = true;
229 SVGObserverUtils::InvalidateDirectRenderingObservers(mCanvasElement);
231 mCanvasElement->InvalidateCanvasContent(nullptr);
234 void WebGLContext::OnMemoryPressure() {
235 bool shouldLoseContext = mLoseContextOnMemoryPressure;
237 if (!mCanLoseContextInForeground &&
238 ProcessPriorityManager::CurrentProcessIsForeground()) {
239 shouldLoseContext = false;
242 if (shouldLoseContext) LoseContext();
245 // --
247 bool WebGLContext::CreateAndInitGL(
248 bool forceEnabled, std::vector<FailureReason>* const out_failReasons) {
249 const FuncScope funcScope(*this, "<Create>");
251 // Can't use WebGL in headless mode.
252 if (gfxPlatform::IsHeadless()) {
253 FailureReason reason;
254 reason.info =
255 "Can't use WebGL in headless mode (https://bugzil.la/1375585).";
256 out_failReasons->push_back(reason);
257 GenerateWarning("%s", reason.info.BeginReading());
258 return false;
261 // WebGL2 is separately blocked:
262 if (IsWebGL2() && !forceEnabled) {
263 FailureReason reason;
264 if (!gfx::gfxVars::AllowWebgl2()) {
265 reason.info =
266 "AllowWebgl2:false restricts context creation on this system.";
267 out_failReasons->push_back(reason);
268 GenerateWarning("%s", reason.info.BeginReading());
269 return false;
273 gl::CreateContextFlags flags = (gl::CreateContextFlags::NO_VALIDATION |
274 gl::CreateContextFlags::PREFER_ROBUSTNESS);
275 bool tryNativeGL = true;
276 bool tryANGLE = false;
278 if (forceEnabled) {
279 flags |= gl::CreateContextFlags::FORCE_ENABLE_HARDWARE;
282 if (StaticPrefs::webgl_cgl_multithreaded()) {
283 flags |= gl::CreateContextFlags::PREFER_MULTITHREADED;
286 if (IsWebGL2()) {
287 flags |= gl::CreateContextFlags::PREFER_ES3;
288 } else {
289 // Request and prefer ES2 context for WebGL1.
290 flags |= gl::CreateContextFlags::PREFER_EXACT_VERSION;
292 if (!StaticPrefs::webgl_1_allow_core_profiles()) {
293 flags |= gl::CreateContextFlags::REQUIRE_COMPAT_PROFILE;
298 auto powerPref = mOptions.powerPreference;
300 // If "Use hardware acceleration when available" option is disabled:
301 if (!gfx::gfxConfig::IsEnabled(gfx::Feature::HW_COMPOSITING)) {
302 powerPref = dom::WebGLPowerPreference::Low_power;
305 const auto overrideVal = StaticPrefs::webgl_power_preference_override();
306 if (overrideVal > 0) {
307 powerPref = dom::WebGLPowerPreference::High_performance;
308 } else if (overrideVal < 0) {
309 powerPref = dom::WebGLPowerPreference::Low_power;
312 if (powerPref == dom::WebGLPowerPreference::High_performance) {
313 flags |= gl::CreateContextFlags::HIGH_POWER;
317 if (!gfx::gfxVars::WebglAllowCoreProfile()) {
318 flags |= gl::CreateContextFlags::REQUIRE_COMPAT_PROFILE;
321 // --
323 const bool useEGL = PR_GetEnv("MOZ_WEBGL_FORCE_EGL");
325 #ifdef XP_WIN
326 tryNativeGL = false;
327 tryANGLE = true;
329 if (StaticPrefs::webgl_disable_wgl()) {
330 tryNativeGL = false;
333 if (StaticPrefs::webgl_disable_angle() ||
334 PR_GetEnv("MOZ_WEBGL_FORCE_OPENGL") || useEGL) {
335 tryNativeGL = true;
336 tryANGLE = false;
338 #endif
340 if (tryNativeGL && !forceEnabled) {
341 FailureReason reason;
342 if (!gfx::gfxVars::WebglAllowWindowsNativeGl()) {
343 reason.info =
344 "WebglAllowWindowsNativeGl:false restricts context creation on this "
345 "system.";
347 out_failReasons->push_back(reason);
349 GenerateWarning("%s", reason.info.BeginReading());
350 tryNativeGL = false;
354 // --
356 typedef decltype(gl::GLContextProviderEGL::CreateHeadless) fnCreateT;
357 const auto fnCreate = [&](fnCreateT* const pfnCreate,
358 const char* const info) {
359 nsCString failureId;
360 const RefPtr<gl::GLContext> gl = pfnCreate({flags}, &failureId);
361 if (!gl) {
362 out_failReasons->push_back(WebGLContext::FailureReason(failureId, info));
364 return gl;
367 const auto newGL = [&]() -> RefPtr<gl::GLContext> {
368 if (tryNativeGL) {
369 if (useEGL)
370 return fnCreate(&gl::GLContextProviderEGL::CreateHeadless, "useEGL");
372 const auto ret =
373 fnCreate(&gl::GLContextProvider::CreateHeadless, "tryNativeGL");
374 if (ret) return ret;
377 if (tryANGLE) {
378 return fnCreate(&gl::GLContextProviderEGL::CreateHeadless, "tryANGLE");
380 return nullptr;
381 }();
383 if (!newGL) {
384 out_failReasons->push_back(
385 FailureReason("FEATURE_FAILURE_WEBGL_EXHAUSTED_DRIVERS",
386 "Exhausted GL driver options."));
387 return false;
390 // --
392 FailureReason reason;
394 mGL_OnlyClearInDestroyResourcesAndContext = newGL;
395 MOZ_RELEASE_ASSERT(gl);
396 if (!InitAndValidateGL(&reason)) {
397 DestroyResourcesAndContext();
398 MOZ_RELEASE_ASSERT(!gl);
400 // The fail reason here should be specific enough for now.
401 out_failReasons->push_back(reason);
402 return false;
405 const auto val = StaticPrefs::webgl_debug_incomplete_tex_color();
406 if (val) {
407 mIncompleteTexOverride.reset(new gl::Texture(*gl));
408 const gl::ScopedBindTexture autoBind(gl, mIncompleteTexOverride->name);
409 const auto heapVal = std::make_unique<uint32_t>(val);
410 gl->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGBA, 1, 1, 0,
411 LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, heapVal.get());
414 return true;
417 // Fallback for resizes:
419 bool WebGLContext::EnsureDefaultFB() {
420 if (mDefaultFB) {
421 MOZ_ASSERT(*uvec2::FromSize(mDefaultFB->mSize) == mRequestedSize);
422 return true;
425 const bool depthStencil = mOptions.depth || mOptions.stencil;
426 auto attemptSize = gfx::IntSize{mRequestedSize.x, mRequestedSize.y};
428 while (attemptSize.width || attemptSize.height) {
429 attemptSize.width = std::max(attemptSize.width, 1);
430 attemptSize.height = std::max(attemptSize.height, 1);
432 [&]() {
433 if (mOptions.antialias) {
434 MOZ_ASSERT(!mDefaultFB);
435 mDefaultFB = gl::MozFramebuffer::Create(gl, attemptSize, mMsaaSamples,
436 depthStencil);
437 if (mDefaultFB) return;
438 if (mOptionsFrozen) return;
441 MOZ_ASSERT(!mDefaultFB);
442 mDefaultFB = gl::MozFramebuffer::Create(gl, attemptSize, 0, depthStencil);
443 }();
445 if (mDefaultFB) break;
447 attemptSize.width /= 2;
448 attemptSize.height /= 2;
451 if (!mDefaultFB) {
452 GenerateWarning("Backbuffer resize failed. Losing context.");
453 LoseContext();
454 return false;
457 mDefaultFB_IsInvalid = true;
459 const auto actualSize = *uvec2::FromSize(mDefaultFB->mSize);
460 if (actualSize != mRequestedSize) {
461 GenerateWarning(
462 "Requested size %ux%u was too large, but resize"
463 " to %ux%u succeeded.",
464 mRequestedSize.x, mRequestedSize.y, actualSize.x, actualSize.y);
466 mRequestedSize = actualSize;
467 return true;
470 void WebGLContext::Resize(uvec2 requestedSize) {
471 // Zero-sized surfaces can cause problems.
472 if (!requestedSize.x) {
473 requestedSize.x = 1;
475 if (!requestedSize.y) {
476 requestedSize.y = 1;
479 // Kill our current default fb(s), for later lazy allocation.
480 mRequestedSize = requestedSize;
481 mDefaultFB = nullptr;
482 mResetLayer = true; // New size means new Layer.
485 UniquePtr<webgl::FormatUsageAuthority> WebGLContext::CreateFormatUsage(
486 gl::GLContext* gl) const {
487 return webgl::FormatUsageAuthority::CreateForWebGL1(gl);
490 /*static*/
491 RefPtr<WebGLContext> WebGLContext::Create(HostWebGLContext& host,
492 const webgl::InitContextDesc& desc,
493 webgl::InitContextResult* const out) {
494 nsCString failureId = "FEATURE_FAILURE_WEBGL_UNKOWN"_ns;
495 const bool forceEnabled = StaticPrefs::webgl_force_enabled();
496 ScopedGfxFeatureReporter reporter("WebGL", forceEnabled);
498 auto res = [&]() -> Result<RefPtr<WebGLContext>, std::string> {
499 bool disabled = StaticPrefs::webgl_disabled();
501 // TODO: When we have software webgl support we should use that instead.
502 disabled |= gfxPlatform::InSafeMode();
504 if (disabled) {
505 if (gfxPlatform::InSafeMode()) {
506 failureId = "FEATURE_FAILURE_WEBGL_SAFEMODE"_ns;
507 } else {
508 failureId = "FEATURE_FAILURE_WEBGL_DISABLED"_ns;
510 return Err("WebGL is currently disabled.");
513 // Alright, now let's start trying.
515 RefPtr<WebGLContext> webgl;
516 if (desc.isWebgl2) {
517 webgl = new WebGL2Context(host, desc);
518 } else {
519 webgl = new WebGLContext(host, desc);
522 MOZ_ASSERT(!webgl->gl);
523 std::vector<FailureReason> failReasons;
524 if (!webgl->CreateAndInitGL(forceEnabled, &failReasons)) {
525 nsCString text("WebGL creation failed: ");
526 for (const auto& cur : failReasons) {
527 // Don't try to accumulate using an empty key if |cur.key| is empty.
528 if (cur.key.IsEmpty()) {
529 Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_FAILURE_ID,
530 "FEATURE_FAILURE_REASON_UNKNOWN"_ns);
531 } else {
532 Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_FAILURE_ID, cur.key);
535 const auto str = nsPrintfCString("\n* %s (%s)", cur.info.BeginReading(),
536 cur.key.BeginReading());
537 text.Append(str);
539 failureId = "FEATURE_FAILURE_REASON"_ns;
540 return Err(text.BeginReading());
542 MOZ_ASSERT(webgl->gl);
544 if (desc.options.failIfMajorPerformanceCaveat) {
545 if (webgl->gl->IsWARP()) {
546 failureId = "FEATURE_FAILURE_WEBGL_PERF_WARP"_ns;
547 return Err(
548 "failIfMajorPerformanceCaveat: Driver is not"
549 " hardware-accelerated.");
552 #ifdef XP_WIN
553 if (webgl->gl->GetContextType() == gl::GLContextType::WGL &&
554 !gl::sWGLLib.HasDXInterop2()) {
555 failureId = "FEATURE_FAILURE_WEBGL_DXGL_INTEROP2"_ns;
556 return Err("failIfMajorPerformanceCaveat: WGL without DXGLInterop2.");
558 #endif
561 const FuncScope funcScope(*webgl, "getContext/restoreContext");
563 MOZ_ASSERT(!webgl->mDefaultFB);
564 if (!webgl->EnsureDefaultFB()) {
565 MOZ_ASSERT(!webgl->mDefaultFB);
566 MOZ_ASSERT(webgl->IsContextLost());
567 failureId = "FEATURE_FAILURE_WEBGL_BACKBUFFER"_ns;
568 return Err("Initializing WebGL backbuffer failed.");
571 return webgl;
572 }();
573 if (res.isOk()) {
574 failureId = "SUCCESS"_ns;
576 Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_FAILURE_ID, failureId);
578 if (!res.isOk()) {
579 out->error = res.unwrapErr();
580 return nullptr;
582 const auto webgl = res.unwrap();
584 // Update our internal stuff:
585 webgl->FinishInit();
587 reporter.SetSuccessful();
588 if (gl::GLContext::ShouldSpew()) {
589 printf_stderr("--- WebGL context created: %p\n", webgl.get());
592 // -
594 const auto UploadableSdTypes = [&]() {
595 webgl::EnumMask<layers::SurfaceDescriptor::Type> types;
596 if (webgl->gl->IsANGLE()) {
597 types[layers::SurfaceDescriptor::TSurfaceDescriptorD3D10] = true;
598 types[layers::SurfaceDescriptor::TSurfaceDescriptorDXGIYCbCr] = true;
600 if (kIsMacOS) {
601 types[layers::SurfaceDescriptor::TSurfaceDescriptorMacIOSurface] = true;
603 return types;
606 // -
608 out->options = webgl->mOptions;
609 out->limits = *webgl->mLimits;
610 out->uploadableSdTypes = UploadableSdTypes();
612 return webgl;
615 void WebGLContext::FinishInit() {
616 mOptions.antialias &= bool(mDefaultFB->mSamples);
618 if (!mOptions.alpha) {
619 // We always have alpha.
620 mNeedsFakeNoAlpha = true;
623 if (mOptions.depth || mOptions.stencil) {
624 // We always have depth+stencil if we have either.
625 if (!mOptions.depth) {
626 mNeedsFakeNoDepth = true;
628 if (!mOptions.stencil) {
629 mNeedsFakeNoStencil = true;
633 mNeedsFakeNoStencil_UserFBs = false;
634 #ifdef MOZ_WIDGET_COCOA
635 if (!nsCocoaFeatures::IsAtLeastVersion(10, 12) &&
636 gl->Vendor() == gl::GLVendor::Intel) {
637 mNeedsFakeNoStencil_UserFBs = true;
639 #endif
641 mResetLayer = true;
642 mOptionsFrozen = true;
644 //////
645 // Initial setup.
647 gl->mImplicitMakeCurrent = true;
649 const auto& size = mDefaultFB->mSize;
651 mViewportX = mViewportY = 0;
652 mViewportWidth = size.width;
653 mViewportHeight = size.height;
654 gl->fViewport(mViewportX, mViewportY, mViewportWidth, mViewportHeight);
656 mScissorRect = {0, 0, size.width, size.height};
657 mScissorRect.Apply(*gl);
659 //////
660 // Check everything
662 AssertCachedBindings();
663 AssertCachedGlobalState();
665 mShouldPresent = true;
667 //////
669 gl->ResetSyncCallCount("WebGLContext Initialization");
670 LoseLruContextIfLimitExceeded();
673 void WebGLContext::SetCompositableHost(
674 RefPtr<layers::CompositableHost>& aCompositableHost) {
675 mCompositableHost = aCompositableHost;
678 void WebGLContext::LoseLruContextIfLimitExceeded() {
679 const auto maxContexts = std::max(1u, StaticPrefs::webgl_max_contexts());
680 const auto maxContextsPerPrincipal =
681 std::max(1u, StaticPrefs::webgl_max_contexts_per_principal());
683 // it's important to update the index on a new context before losing old
684 // contexts, otherwise new unused contexts would all have index 0 and we
685 // couldn't distinguish older ones when choosing which one to lose first.
686 BumpLru();
689 size_t forPrincipal = 0;
690 for (const auto& context : sWebglLru) {
691 if (context->mPrincipalKey == mPrincipalKey) {
692 forPrincipal += 1;
696 while (forPrincipal > maxContextsPerPrincipal) {
697 const auto text = nsPrintfCString(
698 "Exceeded %u live WebGL contexts for this principal, losing the "
699 "least recently used one.",
700 maxContextsPerPrincipal);
701 mHost->JsWarning(ToString(text));
703 for (const auto& context : sWebglLru) {
704 if (context->mPrincipalKey == mPrincipalKey) {
705 MOZ_ASSERT(context != this);
706 context->LoseContext(webgl::ContextLossReason::None);
707 forPrincipal -= 1;
708 break;
714 auto total = sWebglLru.size();
715 while (total > maxContexts) {
716 const auto text = nsPrintfCString(
717 "Exceeded %u live WebGL contexts, losing the least "
718 "recently used one.",
719 maxContexts);
720 mHost->JsWarning(ToString(text));
722 const auto& context = sWebglLru.front();
723 MOZ_ASSERT(context != this);
724 context->LoseContext(webgl::ContextLossReason::None);
725 total -= 1;
729 // -
731 namespace webgl {
733 ScopedPrepForResourceClear::ScopedPrepForResourceClear(
734 const WebGLContext& webgl_)
735 : webgl(webgl_) {
736 const auto& gl = webgl.gl;
738 if (webgl.mScissorTestEnabled) {
739 gl->fDisable(LOCAL_GL_SCISSOR_TEST);
741 if (webgl.mRasterizerDiscardEnabled) {
742 gl->fDisable(LOCAL_GL_RASTERIZER_DISCARD);
745 // "The clear operation always uses the front stencil write mask
746 // when clearing the stencil buffer."
747 webgl.DoColorMask(0x0f);
748 gl->fDepthMask(true);
749 gl->fStencilMaskSeparate(LOCAL_GL_FRONT, 0xffffffff);
751 gl->fClearColor(0.0f, 0.0f, 0.0f, 0.0f);
752 gl->fClearDepth(1.0f); // Depth formats are always cleared to 1.0f, not 0.0f.
753 gl->fClearStencil(0);
756 ScopedPrepForResourceClear::~ScopedPrepForResourceClear() {
757 const auto& gl = webgl.gl;
759 if (webgl.mScissorTestEnabled) {
760 gl->fEnable(LOCAL_GL_SCISSOR_TEST);
762 if (webgl.mRasterizerDiscardEnabled) {
763 gl->fEnable(LOCAL_GL_RASTERIZER_DISCARD);
766 // DoColorMask() is lazy.
767 gl->fDepthMask(webgl.mDepthWriteMask);
768 gl->fStencilMaskSeparate(LOCAL_GL_FRONT, webgl.mStencilWriteMaskFront);
770 gl->fClearColor(webgl.mColorClearValue[0], webgl.mColorClearValue[1],
771 webgl.mColorClearValue[2], webgl.mColorClearValue[3]);
772 gl->fClearDepth(webgl.mDepthClearValue);
773 gl->fClearStencil(webgl.mStencilClearValue);
776 } // namespace webgl
778 // -
780 void WebGLContext::OnEndOfFrame() {
781 if (StaticPrefs::webgl_perf_spew_frame_allocs()) {
782 GeneratePerfWarning("[webgl.perf.spew-frame-allocs] %" PRIu64
783 " data allocations this frame.",
784 mDataAllocGLCallCount);
786 mDataAllocGLCallCount = 0;
787 gl->ResetSyncCallCount("WebGLContext PresentScreenBuffer");
789 mDrawCallsSinceLastFlush = 0;
791 BumpLru();
794 void WebGLContext::BlitBackbufferToCurDriverFB(
795 const gl::MozFramebuffer* const source) const {
796 DoColorMask(0x0f);
798 if (mScissorTestEnabled) {
799 gl->fDisable(LOCAL_GL_SCISSOR_TEST);
802 [&]() {
803 const auto fb = source ? source : mDefaultFB.get();
805 if (gl->IsSupported(gl::GLFeature::framebuffer_blit)) {
806 gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, fb->mFB);
807 gl->fBlitFramebuffer(0, 0, fb->mSize.width, fb->mSize.height, 0, 0,
808 fb->mSize.width, fb->mSize.height,
809 LOCAL_GL_COLOR_BUFFER_BIT, LOCAL_GL_NEAREST);
810 return;
812 if (mDefaultFB->mSamples &&
813 gl->IsExtensionSupported(
814 gl::GLContext::APPLE_framebuffer_multisample)) {
815 gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, fb->mFB);
816 gl->fResolveMultisampleFramebufferAPPLE();
817 return;
820 gl->BlitHelper()->DrawBlitTextureToFramebuffer(fb->ColorTex(), fb->mSize,
821 fb->mSize);
822 }();
824 if (mScissorTestEnabled) {
825 gl->fEnable(LOCAL_GL_SCISSOR_TEST);
829 // -
831 template <typename T, typename... Args>
832 constexpr auto MakeArray(Args... args) -> std::array<T, sizeof...(Args)> {
833 return {{static_cast<T>(args)...}};
836 // -
838 // For an overview of how WebGL compositing works, see:
839 // https://wiki.mozilla.org/Platform/GFX/WebGL/Compositing
840 bool WebGLContext::PresentInto(gl::SwapChain& swapChain) {
841 OnEndOfFrame();
843 if (!ValidateAndInitFB(nullptr)) return false;
846 auto presenter = swapChain.Acquire(mDefaultFB->mSize);
847 if (!presenter) {
848 GenerateWarning("Swap chain surface creation failed.");
849 LoseContext();
850 return false;
853 const auto destFb = presenter->Fb();
854 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, destFb);
856 BlitBackbufferToCurDriverFB();
858 if (!mOptions.preserveDrawingBuffer) {
859 if (gl->IsSupported(gl::GLFeature::invalidate_framebuffer)) {
860 gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, mDefaultFB->mFB);
861 constexpr auto attachments = MakeArray<GLenum>(
862 LOCAL_GL_COLOR_ATTACHMENT0, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT);
863 gl->fInvalidateFramebuffer(LOCAL_GL_READ_FRAMEBUFFER,
864 attachments.size(), attachments.data());
866 mDefaultFB_IsInvalid = true;
869 #ifdef DEBUG
870 if (!mOptions.alpha) {
871 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, destFb);
872 uint32_t pixel = 0xffbadbad;
873 gl->fReadPixels(0, 0, 1, 1, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE,
874 &pixel);
875 MOZ_ASSERT((pixel & 0xff000000) == 0xff000000);
877 #endif
880 return true;
883 bool WebGLContext::PresentIntoXR(gl::SwapChain& swapChain,
884 const gl::MozFramebuffer& fb) {
885 OnEndOfFrame();
887 auto presenter = swapChain.Acquire(fb.mSize);
888 if (!presenter) {
889 GenerateWarning("Swap chain surface creation failed.");
890 LoseContext();
891 return false;
894 const auto destFb = presenter->Fb();
895 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, destFb);
897 BlitBackbufferToCurDriverFB(&fb);
899 // https://immersive-web.github.io/webxr/#opaque-framebuffer
900 // Opaque framebuffers will always be cleared regardless of the
901 // associated WebGL context’s preserveDrawingBuffer value.
902 if (gl->IsSupported(gl::GLFeature::invalidate_framebuffer)) {
903 gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, fb.mFB);
904 constexpr auto attachments = MakeArray<GLenum>(
905 LOCAL_GL_COLOR_ATTACHMENT0, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT);
906 gl->fInvalidateFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, attachments.size(),
907 attachments.data());
910 return true;
913 void WebGLContext::Present(WebGLFramebuffer* const xrFb,
914 const layers::TextureType consumerType,
915 const bool webvr) {
916 const FuncScope funcScope(*this, "<Present>");
917 if (IsContextLost()) return;
919 auto swapChain = webvr ? &mWebVRSwapChain : &mSwapChain;
920 if (xrFb) {
921 swapChain = &xrFb->mOpaqueSwapChain;
923 const gl::MozFramebuffer* maybeFB = nullptr;
924 if (xrFb) {
925 swapChain = &xrFb->mOpaqueSwapChain;
926 maybeFB = xrFb->mOpaque.get();
927 } else {
928 mResolvedDefaultFB = nullptr;
931 if (!swapChain->mFactory) {
932 auto typedFactory = gl::SurfaceFactory::Create(gl, consumerType);
933 if (typedFactory) {
934 swapChain->mFactory = std::move(typedFactory);
937 if (!swapChain->mFactory) {
938 NS_WARNING("Failed to make an ideal SurfaceFactory.");
939 swapChain->mFactory = MakeUnique<gl::SurfaceFactory_Basic>(*gl);
941 MOZ_ASSERT(swapChain->mFactory);
943 if (maybeFB) {
944 (void)PresentIntoXR(*swapChain, *maybeFB);
945 } else {
946 (void)PresentInto(*swapChain);
950 Maybe<layers::SurfaceDescriptor> WebGLContext::GetFrontBuffer(
951 WebGLFramebuffer* const xrFb, const bool webvr) {
952 auto swapChain = webvr ? &mWebVRSwapChain : &mSwapChain;
953 if (xrFb) {
954 swapChain = &xrFb->mOpaqueSwapChain;
956 const auto& front = swapChain->FrontBuffer();
957 if (!front) return {};
959 return front->ToSurfaceDescriptor();
962 bool WebGLContext::FrontBufferSnapshotInto(Range<uint8_t> dest) {
963 const auto& front = mSwapChain.FrontBuffer();
964 if (!front) return false;
966 // -
968 front->WaitForBufferOwnership();
969 front->LockProd();
970 front->ProducerReadAcquire();
971 auto reset = MakeScopeExit([&] {
972 front->ProducerReadRelease();
973 front->UnlockProd();
976 // -
978 gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, 1);
979 if (IsWebGL2()) {
980 gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, 0);
981 gl->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, 0);
982 gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, 0);
985 // -
987 const auto readFbWas = mBoundReadFramebuffer;
988 const auto pboWas = mBoundPixelPackBuffer;
990 GLenum fbTarget = LOCAL_GL_READ_FRAMEBUFFER;
991 if (!IsWebGL2()) {
992 fbTarget = LOCAL_GL_FRAMEBUFFER;
995 gl->fBindFramebuffer(fbTarget,
996 front->mFb ? front->mFb->mFB : mDefaultFB->mFB);
997 if (pboWas) {
998 BindBuffer(LOCAL_GL_PIXEL_PACK_BUFFER, nullptr);
1001 auto reset2 = MakeScopeExit([&] {
1002 DoBindFB(readFbWas, fbTarget);
1003 if (pboWas) {
1004 BindBuffer(LOCAL_GL_PIXEL_PACK_BUFFER, pboWas);
1008 const auto& size = front->mDesc.size;
1009 const size_t stride = size.width * 4;
1010 MOZ_ASSERT(dest.length() == stride * size.height);
1011 gl->fReadPixels(0, 0, size.width, size.height, LOCAL_GL_RGBA,
1012 LOCAL_GL_UNSIGNED_BYTE, dest.begin().get());
1013 gfxUtils::ConvertBGRAtoRGBA(dest.begin().get(), stride * size.height);
1015 return true;
1018 void WebGLContext::ClearVRSwapChain() { mWebVRSwapChain.ClearPool(); }
1020 // ------------------------
1022 RefPtr<gfx::DataSourceSurface> GetTempSurface(const gfx::IntSize& aSize,
1023 gfx::SurfaceFormat& aFormat) {
1024 uint32_t stride =
1025 gfx::GetAlignedStride<8>(aSize.width, BytesPerPixel(aFormat));
1026 return gfx::Factory::CreateDataSourceSurfaceWithStride(aSize, aFormat,
1027 stride);
1030 void WebGLContext::DummyReadFramebufferOperation() {
1031 if (!mBoundReadFramebuffer) return; // Infallible.
1033 const auto status = mBoundReadFramebuffer->CheckFramebufferStatus();
1034 if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
1035 ErrorInvalidFramebufferOperation("Framebuffer must be complete.");
1039 bool WebGLContext::Has64BitTimestamps() const {
1040 // 'sync' provides glGetInteger64v either by supporting ARB_sync, GL3+, or
1041 // GLES3+.
1042 return gl->IsSupported(gl::GLFeature::sync);
1045 static bool CheckContextLost(gl::GLContext* gl, bool* const out_isGuilty) {
1046 MOZ_ASSERT(gl);
1048 const auto resetStatus = gl->fGetGraphicsResetStatus();
1049 if (resetStatus == LOCAL_GL_NO_ERROR) {
1050 *out_isGuilty = false;
1051 return false;
1054 // Assume guilty unless we find otherwise!
1055 bool isGuilty = true;
1056 switch (resetStatus) {
1057 case LOCAL_GL_INNOCENT_CONTEXT_RESET_ARB:
1058 case LOCAL_GL_PURGED_CONTEXT_RESET_NV:
1059 // Either nothing wrong, or not our fault.
1060 isGuilty = false;
1061 break;
1062 case LOCAL_GL_GUILTY_CONTEXT_RESET_ARB:
1063 NS_WARNING(
1064 "WebGL content on the page definitely caused the graphics"
1065 " card to reset.");
1066 break;
1067 case LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB:
1068 NS_WARNING(
1069 "WebGL content on the page might have caused the graphics"
1070 " card to reset");
1071 // If we can't tell, assume not-guilty.
1072 // Todo: Implement max number of "unknown" resets per document or time.
1073 isGuilty = false;
1074 break;
1075 default:
1076 gfxCriticalError() << "Unexpected glGetGraphicsResetStatus: "
1077 << gfx::hexa(resetStatus);
1078 break;
1081 if (isGuilty) {
1082 NS_WARNING(
1083 "WebGL context on this page is considered guilty, and will"
1084 " not be restored.");
1087 *out_isGuilty = isGuilty;
1088 return true;
1091 void WebGLContext::RunContextLossTimer() { mContextLossHandler.RunTimer(); }
1093 // We use this timer for many things. Here are the things that it is activated
1094 // for:
1095 // 1) If a script is using the MOZ_WEBGL_lose_context extension.
1096 // 2) If we are using EGL and _NOT ANGLE_, we query periodically to see if the
1097 // CONTEXT_LOST_WEBGL error has been triggered.
1098 // 3) If we are using ANGLE, or anything that supports ARB_robustness, query the
1099 // GPU periodically to see if the reset status bit has been set.
1100 // In all of these situations, we use this timer to send the script context lost
1101 // and restored events asynchronously. For example, if it triggers a context
1102 // loss, the webglcontextlost event will be sent to it the next time the
1103 // robustness timer fires.
1104 // Note that this timer mechanism is not used unless one of these 3 criteria are
1105 // met.
1106 // At a bare minimum, from context lost to context restores, it would take 3
1107 // full timer iterations: detection, webglcontextlost, webglcontextrestored.
1108 void WebGLContext::CheckForContextLoss() {
1109 bool isGuilty = true;
1110 const auto isContextLost = CheckContextLost(gl, &isGuilty);
1111 if (!isContextLost) return;
1113 mWebGLError = LOCAL_GL_CONTEXT_LOST;
1115 auto reason = webgl::ContextLossReason::None;
1116 if (isGuilty) {
1117 reason = webgl::ContextLossReason::Guilty;
1119 LoseContext(reason);
1122 void WebGLContext::LoseContext(const webgl::ContextLossReason reason) {
1123 printf_stderr("WebGL(%p)::LoseContext(%u)\n", this,
1124 static_cast<uint32_t>(reason));
1125 mIsContextLost = true;
1126 mLruPosition = {};
1127 mHost->OnContextLoss(reason);
1130 void WebGLContext::DidRefresh() {
1131 if (gl) {
1132 gl->FlushIfHeavyGLCallsSinceLastFlush();
1136 ////////////////////////////////////////////////////////////////////////////////
1138 uvec2 WebGLContext::DrawingBufferSize() {
1139 const FuncScope funcScope(*this, "width/height");
1140 if (IsContextLost()) return {};
1142 if (!EnsureDefaultFB()) return {};
1144 return *uvec2::FromSize(mDefaultFB->mSize);
1147 bool WebGLContext::ValidateAndInitFB(const WebGLFramebuffer* const fb,
1148 const GLenum incompleteFbError) {
1149 if (fb) return fb->ValidateAndInitAttachments(incompleteFbError);
1151 if (!EnsureDefaultFB()) return false;
1153 if (mDefaultFB_IsInvalid) {
1154 // Clear it!
1155 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mDefaultFB->mFB);
1156 const webgl::ScopedPrepForResourceClear scopedPrep(*this);
1157 if (!mOptions.alpha) {
1158 gl->fClearColor(0, 0, 0, 1);
1160 const GLbitfield bits = LOCAL_GL_COLOR_BUFFER_BIT |
1161 LOCAL_GL_DEPTH_BUFFER_BIT |
1162 LOCAL_GL_STENCIL_BUFFER_BIT;
1163 gl->fClear(bits);
1165 mDefaultFB_IsInvalid = false;
1167 return true;
1170 void WebGLContext::DoBindFB(const WebGLFramebuffer* const fb,
1171 const GLenum target) const {
1172 const GLenum driverFB = fb ? fb->mGLName : mDefaultFB->mFB;
1173 gl->fBindFramebuffer(target, driverFB);
1176 bool WebGLContext::BindCurFBForDraw() {
1177 const auto& fb = mBoundDrawFramebuffer;
1178 if (!ValidateAndInitFB(fb)) return false;
1180 DoBindFB(fb);
1181 return true;
1184 bool WebGLContext::BindCurFBForColorRead(
1185 const webgl::FormatUsageInfo** const out_format, uint32_t* const out_width,
1186 uint32_t* const out_height, const GLenum incompleteFbError) {
1187 const auto& fb = mBoundReadFramebuffer;
1189 if (fb) {
1190 if (!ValidateAndInitFB(fb, incompleteFbError)) return false;
1191 if (!fb->ValidateForColorRead(out_format, out_width, out_height))
1192 return false;
1194 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fb->mGLName);
1195 return true;
1198 if (!BindDefaultFBForRead()) return false;
1200 if (mDefaultFB_ReadBuffer == LOCAL_GL_NONE) {
1201 ErrorInvalidOperation(
1202 "Can't read from backbuffer when readBuffer mode is NONE.");
1203 return false;
1206 auto effFormat = mOptions.alpha ? webgl::EffectiveFormat::RGBA8
1207 : webgl::EffectiveFormat::RGB8;
1209 *out_format = mFormatUsage->GetUsage(effFormat);
1210 MOZ_ASSERT(*out_format);
1212 *out_width = mDefaultFB->mSize.width;
1213 *out_height = mDefaultFB->mSize.height;
1214 return true;
1217 bool WebGLContext::BindDefaultFBForRead() {
1218 if (!ValidateAndInitFB(nullptr)) return false;
1220 if (!mDefaultFB->mSamples) {
1221 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mDefaultFB->mFB);
1222 return true;
1225 if (!mResolvedDefaultFB) {
1226 mResolvedDefaultFB =
1227 gl::MozFramebuffer::Create(gl, mDefaultFB->mSize, 0, false);
1228 if (!mResolvedDefaultFB) {
1229 gfxCriticalNote << FuncName() << ": Failed to create mResolvedDefaultFB.";
1230 return false;
1234 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mResolvedDefaultFB->mFB);
1235 BlitBackbufferToCurDriverFB();
1237 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mResolvedDefaultFB->mFB);
1238 return true;
1241 void WebGLContext::DoColorMask(const uint8_t bitmask) const {
1242 if (mDriverColorMask != bitmask) {
1243 mDriverColorMask = bitmask;
1244 gl->fColorMask(
1245 bool(mDriverColorMask & (1 << 0)), bool(mDriverColorMask & (1 << 1)),
1246 bool(mDriverColorMask & (1 << 2)), bool(mDriverColorMask & (1 << 3)));
1250 ////////////////////////////////////////////////////////////////////////////////
1252 ScopedDrawCallWrapper::ScopedDrawCallWrapper(WebGLContext& webgl)
1253 : mWebGL(webgl) {
1254 uint8_t driverColorMask = mWebGL.mColorWriteMask;
1255 bool driverDepthTest = mWebGL.mDepthTestEnabled;
1256 bool driverStencilTest = mWebGL.mStencilTestEnabled;
1257 const auto& fb = mWebGL.mBoundDrawFramebuffer;
1258 if (!fb) {
1259 if (mWebGL.mDefaultFB_DrawBuffer0 == LOCAL_GL_NONE) {
1260 driverColorMask = 0; // Is this well-optimized enough for depth-first
1261 // rendering?
1262 } else {
1263 driverColorMask &= ~(uint8_t(mWebGL.mNeedsFakeNoAlpha) << 3);
1265 driverDepthTest &= !mWebGL.mNeedsFakeNoDepth;
1266 driverStencilTest &= !mWebGL.mNeedsFakeNoStencil;
1267 } else {
1268 if (mWebGL.mNeedsFakeNoStencil_UserFBs &&
1269 fb->DepthAttachment().HasAttachment() &&
1270 !fb->StencilAttachment().HasAttachment()) {
1271 driverStencilTest = false;
1275 const auto& gl = mWebGL.gl;
1276 mWebGL.DoColorMask(driverColorMask);
1277 if (mWebGL.mDriverDepthTest != driverDepthTest) {
1278 // "When disabled, the depth comparison and subsequent possible updates to
1279 // the
1280 // depth buffer value are bypassed and the fragment is passed to the next
1281 // operation." [GLES 3.0.5, p177]
1282 mWebGL.mDriverDepthTest = driverDepthTest;
1283 gl->SetEnabled(LOCAL_GL_DEPTH_TEST, mWebGL.mDriverDepthTest);
1285 if (mWebGL.mDriverStencilTest != driverStencilTest) {
1286 // "When disabled, the stencil test and associated modifications are not
1287 // made, and
1288 // the fragment is always passed." [GLES 3.0.5, p175]
1289 mWebGL.mDriverStencilTest = driverStencilTest;
1290 gl->SetEnabled(LOCAL_GL_STENCIL_TEST, mWebGL.mDriverStencilTest);
1294 ScopedDrawCallWrapper::~ScopedDrawCallWrapper() {
1295 if (mWebGL.mBoundDrawFramebuffer) return;
1297 mWebGL.mResolvedDefaultFB = nullptr;
1298 mWebGL.mShouldPresent = true;
1301 // -
1303 void WebGLContext::ScissorRect::Apply(gl::GLContext& gl) const {
1304 gl.fScissor(x, y, w, h);
1307 ////////////////////////////////////////
1309 IndexedBufferBinding::IndexedBufferBinding() : mRangeStart(0), mRangeSize(0) {}
1311 uint64_t IndexedBufferBinding::ByteCount() const {
1312 if (!mBufferBinding) return 0;
1314 uint64_t bufferSize = mBufferBinding->ByteLength();
1315 if (!mRangeSize) // BindBufferBase
1316 return bufferSize;
1318 if (mRangeStart >= bufferSize) return 0;
1319 bufferSize -= mRangeStart;
1321 return std::min(bufferSize, mRangeSize);
1324 ////////////////////////////////////////
1326 ScopedFBRebinder::~ScopedFBRebinder() {
1327 const auto fnName = [&](WebGLFramebuffer* fb) {
1328 return fb ? fb->mGLName : 0;
1331 const auto& gl = mWebGL->gl;
1332 if (mWebGL->IsWebGL2()) {
1333 gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER,
1334 fnName(mWebGL->mBoundDrawFramebuffer));
1335 gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER,
1336 fnName(mWebGL->mBoundReadFramebuffer));
1337 } else {
1338 MOZ_ASSERT(mWebGL->mBoundDrawFramebuffer == mWebGL->mBoundReadFramebuffer);
1339 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER,
1340 fnName(mWebGL->mBoundDrawFramebuffer));
1344 ////////////////////
1346 void DoBindBuffer(gl::GLContext& gl, const GLenum target,
1347 const WebGLBuffer* const buffer) {
1348 gl.fBindBuffer(target, buffer ? buffer->mGLName : 0);
1351 ////////////////////////////////////////
1353 bool Intersect(const int32_t srcSize, const int32_t read0,
1354 const int32_t readSize, int32_t* const out_intRead0,
1355 int32_t* const out_intWrite0, int32_t* const out_intSize) {
1356 MOZ_ASSERT(srcSize >= 0);
1357 MOZ_ASSERT(readSize >= 0);
1358 const auto read1 = int64_t(read0) + readSize;
1360 int32_t intRead0 = read0; // Clearly doesn't need validation.
1361 int64_t intWrite0 = 0;
1362 int64_t intSize = readSize;
1364 if (read1 <= 0 || read0 >= srcSize) {
1365 // Disjoint ranges.
1366 intSize = 0;
1367 } else {
1368 if (read0 < 0) {
1369 const auto diff = int64_t(0) - read0;
1370 MOZ_ASSERT(diff >= 0);
1371 intRead0 = 0;
1372 intWrite0 = diff;
1373 intSize -= diff;
1375 if (read1 > srcSize) {
1376 const auto diff = int64_t(read1) - srcSize;
1377 MOZ_ASSERT(diff >= 0);
1378 intSize -= diff;
1381 if (!CheckedInt<int32_t>(intWrite0).isValid() ||
1382 !CheckedInt<int32_t>(intSize).isValid()) {
1383 return false;
1387 *out_intRead0 = intRead0;
1388 *out_intWrite0 = intWrite0;
1389 *out_intSize = intSize;
1390 return true;
1393 // --
1395 uint64_t AvailGroups(const uint64_t totalAvailItems,
1396 const uint64_t firstItemOffset, const uint32_t groupSize,
1397 const uint32_t groupStride) {
1398 MOZ_ASSERT(groupSize && groupStride);
1399 MOZ_ASSERT(groupSize <= groupStride);
1401 if (totalAvailItems <= firstItemOffset) return 0;
1402 const size_t availItems = totalAvailItems - firstItemOffset;
1404 size_t availGroups = availItems / groupStride;
1405 const size_t tailItems = availItems % groupStride;
1406 if (tailItems >= groupSize) {
1407 availGroups += 1;
1409 return availGroups;
1412 ////////////////////////////////////////////////////////////////////////////////
1414 const char* WebGLContext::FuncName() const {
1415 const char* ret;
1416 if (MOZ_LIKELY(mFuncScope)) {
1417 ret = mFuncScope->mFuncName;
1418 } else {
1419 NS_WARNING("FuncScope not on stack!");
1420 ret = "<unknown function>";
1422 return ret;
1425 // -
1427 WebGLContext::FuncScope::FuncScope(const WebGLContext& webgl,
1428 const char* const funcName)
1429 : mWebGL(webgl), mFuncName(bool(mWebGL.mFuncScope) ? nullptr : funcName) {
1430 if (!mFuncName) return;
1431 mWebGL.mFuncScope = this;
1434 WebGLContext::FuncScope::~FuncScope() {
1435 if (mBindFailureGuard) {
1436 gfxCriticalError() << "mBindFailureGuard failure: Early exit from "
1437 << mWebGL.FuncName();
1440 if (!mFuncName) return;
1441 mWebGL.mFuncScope = nullptr;
1444 // --
1446 bool ClientWebGLContext::IsXRCompatible() const { return mXRCompatible; }
1448 already_AddRefed<dom::Promise> ClientWebGLContext::MakeXRCompatible(
1449 ErrorResult& aRv) {
1450 const FuncScope funcScope(*this, "MakeXRCompatible");
1451 nsCOMPtr<nsIGlobalObject> global;
1452 // TODO: Bug 1596921
1453 // Should use nsICanvasRenderingContextInternal::GetParentObject
1454 // once it has been updated to work in the offscreencanvas case
1455 if (mCanvasElement) {
1456 global = GetOwnerDoc()->GetScopeObject();
1457 } else if (mOffscreenCanvas) {
1458 global = mOffscreenCanvas->GetOwnerGlobal();
1460 if (!global) {
1461 aRv.ThrowInvalidAccessError(
1462 "Using a WebGL context that is not attached to either a canvas or an "
1463 "OffscreenCanvas");
1464 return nullptr;
1466 RefPtr<dom::Promise> promise = dom::Promise::Create(global, aRv);
1467 NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
1469 if (IsContextLost()) {
1470 promise->MaybeRejectWithInvalidStateError(
1471 "Can not make context XR compatible when context is already lost.");
1472 return promise.forget();
1475 // TODO: Bug 1580258 - WebGLContext.MakeXRCompatible needs to switch to
1476 // the device connected to the XR hardware
1477 // This should update `options` and lose+restore the context.
1478 mXRCompatible = true;
1479 promise->MaybeResolveWithUndefined();
1480 return promise.forget();
1483 // --
1485 webgl::AvailabilityRunnable& ClientWebGLContext::EnsureAvailabilityRunnable()
1486 const {
1487 if (!mAvailabilityRunnable) {
1488 mAvailabilityRunnable = new webgl::AvailabilityRunnable(this);
1489 auto forgettable = mAvailabilityRunnable;
1490 NS_DispatchToCurrentThread(forgettable.forget());
1492 return *mAvailabilityRunnable;
1495 webgl::AvailabilityRunnable::AvailabilityRunnable(
1496 const ClientWebGLContext* const webgl)
1497 : Runnable("webgl::AvailabilityRunnable"), mWebGL(webgl) {}
1499 webgl::AvailabilityRunnable::~AvailabilityRunnable() {
1500 MOZ_ASSERT(mQueries.empty());
1501 MOZ_ASSERT(mSyncs.empty());
1504 nsresult webgl::AvailabilityRunnable::Run() {
1505 for (const auto& cur : mQueries) {
1506 if (!cur) continue;
1507 cur->mCanBeAvailable = true;
1509 mQueries.clear();
1511 for (const auto& cur : mSyncs) {
1512 if (!cur) continue;
1513 cur->mCanBeAvailable = true;
1515 mSyncs.clear();
1517 if (mWebGL) {
1518 mWebGL->mAvailabilityRunnable = nullptr;
1520 return NS_OK;
1523 // -
1525 void WebGLContext::GenerateErrorImpl(const GLenum errOrWarning,
1526 const std::string& text) const {
1527 auto err = errOrWarning;
1528 bool isPerfWarning = false;
1529 if (err == webgl::kErrorPerfWarning) {
1530 err = 0;
1531 isPerfWarning = true;
1534 if (err && mFuncScope && mFuncScope->mBindFailureGuard) {
1535 gfxCriticalError() << "mBindFailureGuard failure: Generating error "
1536 << EnumString(err) << ": " << text;
1539 /* ES2 section 2.5 "GL Errors" states that implementations can have
1540 * multiple 'flags', as errors might be caught in different parts of
1541 * a distributed implementation.
1542 * We're signing up as a distributed implementation here, with
1543 * separate flags for WebGL and the underlying GLContext.
1545 if (!mWebGLError) mWebGLError = err;
1547 if (!mHost) return; // Impossible?
1549 // -
1551 const auto ShouldWarn = [&]() {
1552 if (isPerfWarning) {
1553 return ShouldGeneratePerfWarnings();
1555 return ShouldGenerateWarnings();
1557 if (!ShouldWarn()) return;
1559 // -
1561 auto* pNumWarnings = &mWarningCount;
1562 const char* warningsType = "warnings";
1563 if (isPerfWarning) {
1564 pNumWarnings = &mNumPerfWarnings;
1565 warningsType = "perf warnings";
1568 if (isPerfWarning) {
1569 const auto perfText = std::string("WebGL perf warning: ") + text;
1570 mHost->JsWarning(perfText);
1571 } else {
1572 mHost->JsWarning(text);
1574 *pNumWarnings += 1;
1576 if (!ShouldWarn()) {
1577 const auto& msg = nsPrintfCString(
1578 "After reporting %i, no further %s will be reported for this WebGL "
1579 "context.",
1580 int(*pNumWarnings), warningsType);
1581 mHost->JsWarning(ToString(msg));
1585 // -
1587 Maybe<std::string> WebGLContext::GetString(const GLenum pname) const {
1588 const WebGLContext::FuncScope funcScope(*this, "getParameter");
1589 if (IsContextLost()) return {};
1591 const auto FromRaw = [](const char* const raw) -> Maybe<std::string> {
1592 if (!raw) return {};
1593 return Some(std::string(raw));
1596 switch (pname) {
1597 case LOCAL_GL_EXTENSIONS: {
1598 if (!gl->IsCoreProfile()) {
1599 const auto rawExt = (const char*)gl->fGetString(LOCAL_GL_EXTENSIONS);
1600 return FromRaw(rawExt);
1602 std::string ret;
1603 const auto& numExts = gl->GetIntAs<GLuint>(LOCAL_GL_NUM_EXTENSIONS);
1604 for (GLuint i = 0; i < numExts; i++) {
1605 const auto rawExt =
1606 (const char*)gl->fGetStringi(LOCAL_GL_EXTENSIONS, i);
1607 if (!rawExt) continue;
1609 if (i > 0) {
1610 ret += " ";
1612 ret += rawExt;
1614 return Some(std::move(ret));
1617 case LOCAL_GL_RENDERER:
1618 case LOCAL_GL_VENDOR:
1619 case LOCAL_GL_VERSION: {
1620 const auto raw = (const char*)gl->fGetString(pname);
1621 return FromRaw(raw);
1624 case dom::MOZ_debug_Binding::WSI_INFO: {
1625 nsCString info;
1626 gl->GetWSIInfo(&info);
1627 return Some(std::string(info.BeginReading()));
1630 default:
1631 ErrorInvalidEnumArg("pname", pname);
1632 return {};
1636 // ---------------------------------
1638 Maybe<webgl::IndexedName> webgl::ParseIndexed(const std::string& str) {
1639 static const std::regex kRegex("(.*)\\[([0-9]+)\\]");
1641 std::smatch match;
1642 if (!std::regex_match(str, match, kRegex)) return {};
1644 const auto index = std::stoull(match[2]);
1645 return Some(webgl::IndexedName{match[1], index});
1648 // ExplodeName("foo.bar[3].x") -> ["foo", ".", "bar", "[", "3", "]", ".", "x"]
1649 static std::vector<std::string> ExplodeName(const std::string& str) {
1650 std::vector<std::string> ret;
1652 static const std::regex kSep("[.[\\]]");
1654 auto itr = std::regex_token_iterator<decltype(str.begin())>(
1655 str.begin(), str.end(), kSep, {-1, 0});
1656 const auto end = decltype(itr)();
1658 for (; itr != end; ++itr) {
1659 const auto& part = itr->str();
1660 if (part.size()) {
1661 ret.push_back(part);
1664 return ret;
1669 //#define DUMP_MakeLinkResult
1671 webgl::LinkActiveInfo GetLinkActiveInfo(
1672 gl::GLContext& gl, const GLuint prog, const bool webgl2,
1673 const std::unordered_map<std::string, std::string>& nameUnmap) {
1674 webgl::LinkActiveInfo ret;
1675 [&]() {
1676 const auto fnGetProgramui = [&](const GLenum pname) {
1677 GLint ret = 0;
1678 gl.fGetProgramiv(prog, pname, &ret);
1679 return static_cast<uint32_t>(ret);
1682 std::vector<char> stringBuffer(1);
1683 const auto fnEnsureCapacity = [&](const GLenum pname) {
1684 const auto maxWithNull = fnGetProgramui(pname);
1685 if (maxWithNull > stringBuffer.size()) {
1686 stringBuffer.resize(maxWithNull);
1690 fnEnsureCapacity(LOCAL_GL_ACTIVE_ATTRIBUTE_MAX_LENGTH);
1691 fnEnsureCapacity(LOCAL_GL_ACTIVE_UNIFORM_MAX_LENGTH);
1692 if (webgl2) {
1693 fnEnsureCapacity(LOCAL_GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH);
1694 fnEnsureCapacity(LOCAL_GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH);
1697 // -
1699 const auto fnUnmapName = [&](const std::string& mappedName) {
1700 const auto parts = ExplodeName(mappedName);
1702 std::ostringstream ret;
1703 for (const auto& part : parts) {
1704 const auto maybe = MaybeFind(nameUnmap, part);
1705 if (maybe) {
1706 ret << *maybe;
1707 } else {
1708 ret << part;
1711 return ret.str();
1714 // -
1717 const auto count = fnGetProgramui(LOCAL_GL_ACTIVE_ATTRIBUTES);
1718 ret.activeAttribs.reserve(count);
1719 for (const auto i : IntegerRange(count)) {
1720 GLsizei lengthWithoutNull = 0;
1721 GLint elemCount = 0; // `size`
1722 GLenum elemType = 0; // `type`
1723 gl.fGetActiveAttrib(prog, i, stringBuffer.size(), &lengthWithoutNull,
1724 &elemCount, &elemType, stringBuffer.data());
1725 if (!elemType) {
1726 const auto error = gl.fGetError();
1727 if (error != LOCAL_GL_CONTEXT_LOST) {
1728 gfxCriticalError() << "Failed to do glGetActiveAttrib: " << error;
1730 return;
1732 const auto mappedName =
1733 std::string(stringBuffer.data(), lengthWithoutNull);
1734 const auto userName = fnUnmapName(mappedName);
1736 auto loc = gl.fGetAttribLocation(prog, mappedName.c_str());
1737 if (mappedName.find("gl_") == 0) {
1738 // Bug 1328559: Appears problematic on ANGLE and OSX, but not Linux or
1739 // Win+GL.
1740 loc = -1;
1743 #ifdef DUMP_MakeLinkResult
1744 printf_stderr("[attrib %u/%u] @%i %s->%s\n", i, count, loc,
1745 userName.c_str(), mappedName.c_str());
1746 #endif
1747 webgl::ActiveAttribInfo info;
1748 info.elemType = elemType;
1749 info.elemCount = elemCount;
1750 info.name = userName;
1751 info.location = loc;
1752 info.baseType = webgl::ToAttribBaseType(info.elemType);
1753 ret.activeAttribs.push_back(std::move(info));
1757 // -
1760 const auto count = fnGetProgramui(LOCAL_GL_ACTIVE_UNIFORMS);
1761 ret.activeUniforms.reserve(count);
1763 std::vector<GLint> blockIndexList(count, -1);
1764 std::vector<GLint> blockOffsetList(count, -1);
1765 std::vector<GLint> blockArrayStrideList(count, -1);
1766 std::vector<GLint> blockMatrixStrideList(count, -1);
1767 std::vector<GLint> blockIsRowMajorList(count, 0);
1769 if (webgl2 && count) {
1770 std::vector<GLuint> activeIndices;
1771 activeIndices.reserve(count);
1772 for (const auto i : IntegerRange(count)) {
1773 activeIndices.push_back(i);
1776 gl.fGetActiveUniformsiv(
1777 prog, activeIndices.size(), activeIndices.data(),
1778 LOCAL_GL_UNIFORM_BLOCK_INDEX, blockIndexList.data());
1780 gl.fGetActiveUniformsiv(prog, activeIndices.size(),
1781 activeIndices.data(), LOCAL_GL_UNIFORM_OFFSET,
1782 blockOffsetList.data());
1784 gl.fGetActiveUniformsiv(
1785 prog, activeIndices.size(), activeIndices.data(),
1786 LOCAL_GL_UNIFORM_ARRAY_STRIDE, blockArrayStrideList.data());
1788 gl.fGetActiveUniformsiv(
1789 prog, activeIndices.size(), activeIndices.data(),
1790 LOCAL_GL_UNIFORM_MATRIX_STRIDE, blockMatrixStrideList.data());
1792 gl.fGetActiveUniformsiv(
1793 prog, activeIndices.size(), activeIndices.data(),
1794 LOCAL_GL_UNIFORM_IS_ROW_MAJOR, blockIsRowMajorList.data());
1797 for (const auto i : IntegerRange(count)) {
1798 GLsizei lengthWithoutNull = 0;
1799 GLint elemCount = 0; // `size`
1800 GLenum elemType = 0; // `type`
1801 gl.fGetActiveUniform(prog, i, stringBuffer.size(), &lengthWithoutNull,
1802 &elemCount, &elemType, stringBuffer.data());
1803 if (!elemType) {
1804 const auto error = gl.fGetError();
1805 if (error != LOCAL_GL_CONTEXT_LOST) {
1806 gfxCriticalError() << "Failed to do glGetActiveUniform: " << error;
1808 return;
1810 auto mappedName = std::string(stringBuffer.data(), lengthWithoutNull);
1812 // Get true name
1814 auto baseMappedName = mappedName;
1816 const bool isArray = [&]() {
1817 const auto maybe = webgl::ParseIndexed(mappedName);
1818 if (maybe) {
1819 MOZ_ASSERT(maybe->index == 0);
1820 baseMappedName = std::move(maybe->name);
1821 return true;
1823 return false;
1824 }();
1826 const auto userName = fnUnmapName(mappedName);
1828 // -
1830 webgl::ActiveUniformInfo info;
1831 info.elemType = elemType;
1832 info.elemCount = static_cast<uint32_t>(elemCount);
1833 info.name = userName;
1834 info.block_index = blockIndexList[i];
1835 info.block_offset = blockOffsetList[i];
1836 info.block_arrayStride = blockArrayStrideList[i];
1837 info.block_matrixStride = blockMatrixStrideList[i];
1838 info.block_isRowMajor = bool(blockIsRowMajorList[i]);
1840 #ifdef DUMP_MakeLinkResult
1841 printf_stderr("[uniform %u/%u] %s->%s\n", i + 1, count,
1842 userName.c_str(), mappedName.c_str());
1843 #endif
1845 // Get uniform locations
1847 auto locName = baseMappedName;
1848 const auto baseLength = locName.size();
1849 for (const auto i : IntegerRange(info.elemCount)) {
1850 if (isArray) {
1851 locName.erase(
1852 baseLength); // Erase previous [N], but retain capacity.
1853 locName += '[';
1854 locName += std::to_string(i);
1855 locName += ']';
1857 const auto loc = gl.fGetUniformLocation(prog, locName.c_str());
1858 if (loc != -1) {
1859 info.locByIndex[i] = static_cast<uint32_t>(loc);
1860 #ifdef DUMP_MakeLinkResult
1861 printf_stderr(" [%u] @%i\n", i, loc);
1862 #endif
1865 } // anon
1867 ret.activeUniforms.push_back(std::move(info));
1868 } // for i
1869 } // anon
1871 if (webgl2) {
1872 // -------------------------------------
1873 // active uniform blocks
1875 const auto count = fnGetProgramui(LOCAL_GL_ACTIVE_UNIFORM_BLOCKS);
1876 ret.activeUniformBlocks.reserve(count);
1878 for (const auto i : IntegerRange(count)) {
1879 GLsizei lengthWithoutNull = 0;
1880 gl.fGetActiveUniformBlockName(prog, i, stringBuffer.size(),
1881 &lengthWithoutNull,
1882 stringBuffer.data());
1883 const auto mappedName =
1884 std::string(stringBuffer.data(), lengthWithoutNull);
1885 const auto userName = fnUnmapName(mappedName);
1887 // -
1889 auto info = webgl::ActiveUniformBlockInfo{userName};
1890 GLint val = 0;
1892 gl.fGetActiveUniformBlockiv(prog, i, LOCAL_GL_UNIFORM_BLOCK_DATA_SIZE,
1893 &val);
1894 info.dataSize = static_cast<uint32_t>(val);
1896 gl.fGetActiveUniformBlockiv(
1897 prog, i, LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &val);
1898 info.activeUniformIndices.resize(val);
1899 gl.fGetActiveUniformBlockiv(
1900 prog, i, LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES,
1901 reinterpret_cast<GLint*>(info.activeUniformIndices.data()));
1903 gl.fGetActiveUniformBlockiv(
1904 prog, i, LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER,
1905 &val);
1906 info.referencedByVertexShader = bool(val);
1908 gl.fGetActiveUniformBlockiv(
1909 prog, i, LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER,
1910 &val);
1911 info.referencedByFragmentShader = bool(val);
1913 ret.activeUniformBlocks.push_back(std::move(info));
1914 } // for i
1915 } // anon
1917 // -------------------------------------
1918 // active tf varyings
1920 const auto count = fnGetProgramui(LOCAL_GL_TRANSFORM_FEEDBACK_VARYINGS);
1921 ret.activeTfVaryings.reserve(count);
1923 for (const auto i : IntegerRange(count)) {
1924 GLsizei lengthWithoutNull = 0;
1925 GLsizei elemCount = 0; // `size`
1926 GLenum elemType = 0; // `type`
1927 gl.fGetTransformFeedbackVarying(prog, i, stringBuffer.size(),
1928 &lengthWithoutNull, &elemCount,
1929 &elemType, stringBuffer.data());
1930 const auto mappedName =
1931 std::string(stringBuffer.data(), lengthWithoutNull);
1932 const auto userName = fnUnmapName(mappedName);
1934 ret.activeTfVaryings.push_back(
1935 {elemType, static_cast<uint32_t>(elemCount), userName});
1938 } // if webgl2
1939 }();
1940 return ret;
1943 nsCString ToCString(const std::string& s) {
1944 return nsCString(s.data(), s.size());
1947 webgl::CompileResult WebGLContext::GetCompileResult(
1948 const WebGLShader& shader) const {
1949 webgl::CompileResult ret;
1950 [&]() {
1951 ret.pending = false;
1952 const auto& info = shader.CompileResults();
1953 if (!info) return;
1954 if (!info->mValid) {
1955 ret.log = info->mInfoLog.c_str();
1956 return;
1958 // TODO: These could be large and should be made fallible.
1959 ret.translatedSource = ToCString(info->mObjectCode);
1960 ret.log = ToCString(shader.CompileLog());
1961 if (!shader.IsCompiled()) return;
1962 ret.success = true;
1963 }();
1964 return ret;
1967 webgl::LinkResult WebGLContext::GetLinkResult(const WebGLProgram& prog) const {
1968 webgl::LinkResult ret;
1969 [&]() {
1970 ret.pending = false; // Link status polling not yet implemented.
1971 ret.log = ToCString(prog.LinkLog());
1972 const auto& info = prog.LinkInfo();
1973 if (!info) return;
1974 ret.success = true;
1975 ret.active = info->active;
1976 ret.tfBufferMode = info->transformFeedbackBufferMode;
1977 }();
1978 return ret;
1981 // -
1983 GLint WebGLContext::GetFragDataLocation(const WebGLProgram& prog,
1984 const std::string& userName) const {
1985 const auto err = CheckGLSLVariableName(IsWebGL2(), userName);
1986 if (err) {
1987 GenerateError(err->type, "%s", err->info.c_str());
1988 return -1;
1991 const auto& info = prog.LinkInfo();
1992 if (!info) return -1;
1993 const auto& nameMap = info->nameMap;
1995 const auto parts = ExplodeName(userName);
1997 std::ostringstream ret;
1998 for (const auto& part : parts) {
1999 const auto maybe = MaybeFind(nameMap, part);
2000 if (maybe) {
2001 ret << *maybe;
2002 } else {
2003 ret << part;
2006 const auto mappedName = ret.str();
2008 return gl->fGetFragDataLocation(prog.mGLName, mappedName.c_str());
2011 // -
2013 WebGLContextBoundObject::WebGLContextBoundObject(WebGLContext* webgl)
2014 : mContext(webgl) {}
2016 } // namespace mozilla