Bug 1729395 - Handle message sender going away during message processing r=robwu
[gecko.git] / dom / canvas / WebGLContext.cpp
blobbe54ef7c1c082feddfe38c97f3d85373ad66ea9f
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 <bitset>
10 #include <queue>
11 #include <regex>
13 #include "AccessCheck.h"
14 #include "CompositableHost.h"
15 #include "gfxConfig.h"
16 #include "gfxContext.h"
17 #include "gfxCrashReporterUtils.h"
18 #include "gfxEnv.h"
19 #include "gfxPattern.h"
20 #include "gfxUtils.h"
21 #include "MozFramebuffer.h"
22 #include "GLBlitHelper.h"
23 #include "GLContext.h"
24 #include "GLContextProvider.h"
25 #include "GLReadTexImageHelper.h"
26 #include "GLScreenBuffer.h"
27 #include "ImageContainer.h"
28 #include "ImageEncoder.h"
29 #include "Layers.h"
30 #include "LayerUserData.h"
31 #include "mozilla/dom/BindingUtils.h"
32 #include "mozilla/dom/Document.h"
33 #include "mozilla/dom/Event.h"
34 #include "mozilla/dom/HTMLVideoElement.h"
35 #include "mozilla/dom/ImageData.h"
36 #include "mozilla/dom/WebGLContextEvent.h"
37 #include "mozilla/EnumeratedArrayCycleCollection.h"
38 #include "mozilla/EnumeratedRange.h"
39 #include "mozilla/gfx/gfxVars.h"
40 #include "mozilla/Preferences.h"
41 #include "mozilla/ProcessPriorityManager.h"
42 #include "mozilla/ScopeExit.h"
43 #include "mozilla/Services.h"
44 #include "mozilla/StaticPrefs_webgl.h"
45 #include "mozilla/SVGObserverUtils.h"
46 #include "mozilla/Telemetry.h"
47 #include "nsContentUtils.h"
48 #include "nsDisplayList.h"
49 #include "nsError.h"
50 #include "nsIClassInfoImpl.h"
51 #include "nsIWidget.h"
52 #include "nsServiceManagerUtils.h"
53 #include "SharedSurfaceGL.h"
54 #include "prenv.h"
55 #include "ScopedGLHelpers.h"
56 #include "VRManagerChild.h"
57 #include "mozilla/layers/CompositorBridgeChild.h"
58 #include "mozilla/layers/ImageBridgeChild.h"
59 #include "mozilla/layers/TextureClientSharedSurface.h"
60 #include "mozilla/layers/WebRenderUserData.h"
61 #include "mozilla/layers/WebRenderCanvasRenderer.h"
63 // Local
64 #include "CanvasUtils.h"
65 #include "ClientWebGLContext.h"
66 #include "HostWebGLContext.h"
67 #include "WebGLBuffer.h"
68 #include "WebGLChild.h"
69 #include "WebGLContextLossHandler.h"
70 #include "WebGLContextUtils.h"
71 #include "WebGLExtensions.h"
72 #include "WebGLFormats.h"
73 #include "WebGLFramebuffer.h"
74 #include "WebGLMemoryTracker.h"
75 #include "WebGLObjectModel.h"
76 #include "WebGLProgram.h"
77 #include "WebGLQuery.h"
78 #include "WebGLSampler.h"
79 #include "WebGLShader.h"
80 #include "WebGLShaderValidator.h"
81 #include "WebGLSync.h"
82 #include "WebGLTransformFeedback.h"
83 #include "WebGLValidateStrings.h"
84 #include "WebGLVertexArray.h"
86 #ifdef MOZ_WIDGET_COCOA
87 # include "nsCocoaFeatures.h"
88 #endif
90 #ifdef XP_WIN
91 # include "WGLLibrary.h"
92 #endif
94 // Generated
95 #include "mozilla/dom/WebGLRenderingContextBinding.h"
97 namespace mozilla {
99 WebGLContextOptions::WebGLContextOptions() {
100 // Set default alpha state based on preference.
101 alpha = !StaticPrefs::webgl_default_no_alpha();
102 antialias = StaticPrefs::webgl_default_antialias();
105 bool WebGLContextOptions::operator==(const WebGLContextOptions& r) const {
106 bool eq = true;
107 eq &= (alpha == r.alpha);
108 eq &= (depth == r.depth);
109 eq &= (stencil == r.stencil);
110 eq &= (premultipliedAlpha == r.premultipliedAlpha);
111 eq &= (antialias == r.antialias);
112 eq &= (preserveDrawingBuffer == r.preserveDrawingBuffer);
113 eq &= (failIfMajorPerformanceCaveat == r.failIfMajorPerformanceCaveat);
114 eq &= (xrCompatible == r.xrCompatible);
115 eq &= (powerPreference == r.powerPreference);
116 return eq;
119 static std::list<WebGLContext*> sWebglLru;
121 WebGLContext::LruPosition::LruPosition() : mItr(sWebglLru.end()) {} // NOLINT
123 WebGLContext::LruPosition::LruPosition(WebGLContext& context)
124 : mItr(sWebglLru.insert(sWebglLru.end(), &context)) {}
126 void WebGLContext::LruPosition::reset() {
127 const auto end = sWebglLru.end();
128 if (mItr != end) {
129 sWebglLru.erase(mItr);
130 mItr = end;
134 WebGLContext::WebGLContext(HostWebGLContext& host,
135 const webgl::InitContextDesc& desc)
136 : gl(mGL_OnlyClearInDestroyResourcesAndContext), // const reference
137 mHost(&host),
138 mResistFingerprinting(desc.resistFingerprinting),
139 mOptions(desc.options),
140 mPrincipalKey(desc.principalKey),
141 mMaxPerfWarnings(StaticPrefs::webgl_perf_max_warnings()),
142 mMaxAcceptableFBStatusInvals(
143 StaticPrefs::webgl_perf_max_acceptable_fb_status_invals()),
144 mContextLossHandler(this),
145 mMaxWarnings(StaticPrefs::webgl_max_warnings_per_context()),
146 mAllowFBInvalidation(StaticPrefs::webgl_allow_fb_invalidation()),
147 mMsaaSamples((uint8_t)StaticPrefs::webgl_msaa_samples()),
148 mRequestedSize(desc.size) {
149 host.mContext = this;
150 const FuncScope funcScope(*this, "<Create>");
153 WebGLContext::~WebGLContext() { DestroyResourcesAndContext(); }
155 void WebGLContext::DestroyResourcesAndContext() {
156 if (!gl) return;
158 mDefaultFB = nullptr;
159 mResolvedDefaultFB = nullptr;
161 mBound2DTextures.Clear();
162 mBoundCubeMapTextures.Clear();
163 mBound3DTextures.Clear();
164 mBound2DArrayTextures.Clear();
165 mBoundSamplers.Clear();
166 mBoundArrayBuffer = nullptr;
167 mBoundCopyReadBuffer = nullptr;
168 mBoundCopyWriteBuffer = nullptr;
169 mBoundPixelPackBuffer = nullptr;
170 mBoundPixelUnpackBuffer = nullptr;
171 mBoundTransformFeedbackBuffer = nullptr;
172 mBoundUniformBuffer = nullptr;
173 mCurrentProgram = nullptr;
174 mActiveProgramLinkInfo = nullptr;
175 mBoundDrawFramebuffer = nullptr;
176 mBoundReadFramebuffer = nullptr;
177 mBoundVertexArray = nullptr;
178 mDefaultVertexArray = nullptr;
179 mBoundTransformFeedback = nullptr;
180 mDefaultTransformFeedback = nullptr;
182 mQuerySlot_SamplesPassed = nullptr;
183 mQuerySlot_TFPrimsWritten = nullptr;
184 mQuerySlot_TimeElapsed = nullptr;
186 mIndexedUniformBufferBindings.clear();
188 //////
190 if (mEmptyTFO) {
191 gl->fDeleteTransformFeedbacks(1, &mEmptyTFO);
192 mEmptyTFO = 0;
195 //////
197 if (mFakeVertexAttrib0BufferObject) {
198 gl->fDeleteBuffers(1, &mFakeVertexAttrib0BufferObject);
199 mFakeVertexAttrib0BufferObject = 0;
202 // disable all extensions except "WEBGL_lose_context". see bug #927969
203 // spec: http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
204 for (size_t i = 0; i < size_t(WebGLExtensionID::Max); ++i) {
205 WebGLExtensionID extension = WebGLExtensionID(i);
206 if (extension == WebGLExtensionID::WEBGL_lose_context) continue;
207 mExtensions[extension] = nullptr;
210 // We just got rid of everything, so the context had better
211 // have been going away.
212 if (gl::GLContext::ShouldSpew()) {
213 printf_stderr("--- WebGL context destroyed: %p\n", gl.get());
216 MOZ_ASSERT(gl);
217 gl->MarkDestroyed();
218 mGL_OnlyClearInDestroyResourcesAndContext = nullptr;
219 MOZ_ASSERT(!gl);
222 void ClientWebGLContext::MarkCanvasDirty() {
223 if (!mCanvasElement) return;
225 mCapturedFrameInvalidated = true;
227 if (mIsCanvasDirty) return;
228 mIsCanvasDirty = true;
230 SVGObserverUtils::InvalidateDirectRenderingObservers(mCanvasElement);
232 mCanvasElement->InvalidateCanvasContent(nullptr);
235 void WebGLContext::OnMemoryPressure() {
236 bool shouldLoseContext = mLoseContextOnMemoryPressure;
238 if (!mCanLoseContextInForeground &&
239 ProcessPriorityManager::CurrentProcessIsForeground()) {
240 shouldLoseContext = false;
243 if (shouldLoseContext) LoseContext();
246 // --
248 bool WebGLContext::CreateAndInitGL(
249 bool forceEnabled, std::vector<FailureReason>* const out_failReasons) {
250 const FuncScope funcScope(*this, "<Create>");
252 // WebGL2 is separately blocked:
253 if (IsWebGL2() && !forceEnabled) {
254 FailureReason reason;
255 if (!gfx::gfxVars::AllowWebgl2()) {
256 reason.info =
257 "AllowWebgl2:false restricts context creation on this system.";
258 out_failReasons->push_back(reason);
259 GenerateWarning("%s", reason.info.BeginReading());
260 return false;
264 gl::CreateContextFlags flags = (gl::CreateContextFlags::NO_VALIDATION |
265 gl::CreateContextFlags::PREFER_ROBUSTNESS);
266 bool tryNativeGL = true;
267 bool tryANGLE = false;
269 if (forceEnabled) {
270 flags |= gl::CreateContextFlags::FORCE_ENABLE_HARDWARE;
273 if (StaticPrefs::webgl_cgl_multithreaded()) {
274 flags |= gl::CreateContextFlags::PREFER_MULTITHREADED;
277 if (IsWebGL2()) {
278 flags |= gl::CreateContextFlags::PREFER_ES3;
279 } else {
280 // Request and prefer ES2 context for WebGL1.
281 flags |= gl::CreateContextFlags::PREFER_EXACT_VERSION;
283 if (!StaticPrefs::webgl_1_allow_core_profiles()) {
284 flags |= gl::CreateContextFlags::REQUIRE_COMPAT_PROFILE;
289 auto powerPref = mOptions.powerPreference;
291 // If "Use hardware acceleration when available" option is disabled:
292 if (!gfx::gfxConfig::IsEnabled(gfx::Feature::HW_COMPOSITING)) {
293 powerPref = dom::WebGLPowerPreference::Low_power;
296 const auto overrideVal = StaticPrefs::webgl_power_preference_override();
297 if (overrideVal > 0) {
298 powerPref = dom::WebGLPowerPreference::High_performance;
299 } else if (overrideVal < 0) {
300 powerPref = dom::WebGLPowerPreference::Low_power;
303 if (powerPref == dom::WebGLPowerPreference::High_performance) {
304 flags |= gl::CreateContextFlags::HIGH_POWER;
308 if (!gfx::gfxVars::WebglAllowCoreProfile()) {
309 flags |= gl::CreateContextFlags::REQUIRE_COMPAT_PROFILE;
312 // --
314 const bool useEGL = PR_GetEnv("MOZ_WEBGL_FORCE_EGL");
316 #ifdef XP_WIN
317 tryNativeGL = false;
318 tryANGLE = true;
320 if (StaticPrefs::webgl_disable_wgl()) {
321 tryNativeGL = false;
324 if (StaticPrefs::webgl_disable_angle() ||
325 PR_GetEnv("MOZ_WEBGL_FORCE_OPENGL") || useEGL) {
326 tryNativeGL = true;
327 tryANGLE = false;
329 #endif
331 if (tryNativeGL && !forceEnabled) {
332 FailureReason reason;
333 if (!gfx::gfxVars::WebglAllowWindowsNativeGl()) {
334 reason.info =
335 "WebglAllowWindowsNativeGl:false restricts context creation on this "
336 "system.";
338 out_failReasons->push_back(reason);
340 GenerateWarning("%s", reason.info.BeginReading());
341 tryNativeGL = false;
345 // --
347 using fnCreateT = decltype(gl::GLContextProviderEGL::CreateHeadless);
348 const auto fnCreate = [&](fnCreateT* const pfnCreate,
349 const char* const info) {
350 nsCString failureId;
351 const RefPtr<gl::GLContext> gl = pfnCreate({flags}, &failureId);
352 if (!gl) {
353 out_failReasons->push_back(WebGLContext::FailureReason(failureId, info));
355 return gl;
358 const auto newGL = [&]() -> RefPtr<gl::GLContext> {
359 if (tryNativeGL) {
360 if (useEGL)
361 return fnCreate(&gl::GLContextProviderEGL::CreateHeadless, "useEGL");
363 const auto ret =
364 fnCreate(&gl::GLContextProvider::CreateHeadless, "tryNativeGL");
365 if (ret) return ret;
368 if (tryANGLE) {
369 return fnCreate(&gl::GLContextProviderEGL::CreateHeadless, "tryANGLE");
371 return nullptr;
372 }();
374 if (!newGL) {
375 out_failReasons->push_back(
376 FailureReason("FEATURE_FAILURE_WEBGL_EXHAUSTED_DRIVERS",
377 "Exhausted GL driver options."));
378 return false;
381 // --
383 FailureReason reason;
385 mGL_OnlyClearInDestroyResourcesAndContext = newGL;
386 MOZ_RELEASE_ASSERT(gl);
387 if (!InitAndValidateGL(&reason)) {
388 DestroyResourcesAndContext();
389 MOZ_RELEASE_ASSERT(!gl);
391 // The fail reason here should be specific enough for now.
392 out_failReasons->push_back(reason);
393 return false;
396 const auto val = StaticPrefs::webgl_debug_incomplete_tex_color();
397 if (val) {
398 mIncompleteTexOverride.reset(new gl::Texture(*gl));
399 const gl::ScopedBindTexture autoBind(gl, mIncompleteTexOverride->name);
400 const auto heapVal = std::make_unique<uint32_t>(val);
401 gl->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGBA, 1, 1, 0,
402 LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, heapVal.get());
405 return true;
408 // Fallback for resizes:
410 bool WebGLContext::EnsureDefaultFB() {
411 if (mDefaultFB) {
412 MOZ_ASSERT(*uvec2::FromSize(mDefaultFB->mSize) == mRequestedSize);
413 return true;
416 const bool depthStencil = mOptions.depth || mOptions.stencil;
417 auto attemptSize = gfx::IntSize{mRequestedSize.x, mRequestedSize.y};
419 while (attemptSize.width || attemptSize.height) {
420 attemptSize.width = std::max(attemptSize.width, 1);
421 attemptSize.height = std::max(attemptSize.height, 1);
423 [&]() {
424 if (mOptions.antialias) {
425 MOZ_ASSERT(!mDefaultFB);
426 mDefaultFB = gl::MozFramebuffer::Create(gl, attemptSize, mMsaaSamples,
427 depthStencil);
428 if (mDefaultFB) return;
429 if (mOptionsFrozen) return;
432 MOZ_ASSERT(!mDefaultFB);
433 mDefaultFB = gl::MozFramebuffer::Create(gl, attemptSize, 0, depthStencil);
434 }();
436 if (mDefaultFB) break;
438 attemptSize.width /= 2;
439 attemptSize.height /= 2;
442 if (!mDefaultFB) {
443 GenerateWarning("Backbuffer resize failed. Losing context.");
444 LoseContext();
445 return false;
448 mDefaultFB_IsInvalid = true;
450 const auto actualSize = *uvec2::FromSize(mDefaultFB->mSize);
451 if (actualSize != mRequestedSize) {
452 GenerateWarning(
453 "Requested size %ux%u was too large, but resize"
454 " to %ux%u succeeded.",
455 mRequestedSize.x, mRequestedSize.y, actualSize.x, actualSize.y);
457 mRequestedSize = actualSize;
458 return true;
461 void WebGLContext::Resize(uvec2 requestedSize) {
462 // Zero-sized surfaces can cause problems.
463 if (!requestedSize.x) {
464 requestedSize.x = 1;
466 if (!requestedSize.y) {
467 requestedSize.y = 1;
470 // Kill our current default fb(s), for later lazy allocation.
471 mRequestedSize = requestedSize;
472 mDefaultFB = nullptr;
473 mResetLayer = true; // New size means new Layer.
476 UniquePtr<webgl::FormatUsageAuthority> WebGLContext::CreateFormatUsage(
477 gl::GLContext* gl) const {
478 return webgl::FormatUsageAuthority::CreateForWebGL1(gl);
481 /*static*/
482 RefPtr<WebGLContext> WebGLContext::Create(HostWebGLContext& host,
483 const webgl::InitContextDesc& desc,
484 webgl::InitContextResult* const out) {
485 nsCString failureId = "FEATURE_FAILURE_WEBGL_UNKOWN"_ns;
486 const bool forceEnabled = StaticPrefs::webgl_force_enabled();
487 ScopedGfxFeatureReporter reporter("WebGL", forceEnabled);
489 auto res = [&]() -> Result<RefPtr<WebGLContext>, std::string> {
490 bool disabled = StaticPrefs::webgl_disabled();
492 // TODO: When we have software webgl support we should use that instead.
493 disabled |= gfxPlatform::InSafeMode();
495 if (disabled) {
496 if (gfxPlatform::InSafeMode()) {
497 failureId = "FEATURE_FAILURE_WEBGL_SAFEMODE"_ns;
498 } else {
499 failureId = "FEATURE_FAILURE_WEBGL_DISABLED"_ns;
501 return Err("WebGL is currently disabled.");
504 // Alright, now let's start trying.
506 RefPtr<WebGLContext> webgl;
507 if (desc.isWebgl2) {
508 webgl = new WebGL2Context(host, desc);
509 } else {
510 webgl = new WebGLContext(host, desc);
513 MOZ_ASSERT(!webgl->gl);
514 std::vector<FailureReason> failReasons;
515 if (!webgl->CreateAndInitGL(forceEnabled, &failReasons)) {
516 nsCString text("WebGL creation failed: ");
517 for (const auto& cur : failReasons) {
518 // Don't try to accumulate using an empty key if |cur.key| is empty.
519 if (cur.key.IsEmpty()) {
520 Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_FAILURE_ID,
521 "FEATURE_FAILURE_REASON_UNKNOWN"_ns);
522 } else {
523 Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_FAILURE_ID, cur.key);
526 const auto str = nsPrintfCString("\n* %s (%s)", cur.info.BeginReading(),
527 cur.key.BeginReading());
528 text.Append(str);
530 failureId = "FEATURE_FAILURE_REASON"_ns;
531 return Err(text.BeginReading());
533 MOZ_ASSERT(webgl->gl);
535 if (desc.options.failIfMajorPerformanceCaveat) {
536 if (webgl->gl->IsWARP()) {
537 failureId = "FEATURE_FAILURE_WEBGL_PERF_WARP"_ns;
538 return Err(
539 "failIfMajorPerformanceCaveat: Driver is not"
540 " hardware-accelerated.");
543 #ifdef XP_WIN
544 if (webgl->gl->GetContextType() == gl::GLContextType::WGL &&
545 !gl::sWGLLib.HasDXInterop2()) {
546 failureId = "FEATURE_FAILURE_WEBGL_DXGL_INTEROP2"_ns;
547 return Err("failIfMajorPerformanceCaveat: WGL without DXGLInterop2.");
549 #endif
552 const FuncScope funcScope(*webgl, "getContext/restoreContext");
554 MOZ_ASSERT(!webgl->mDefaultFB);
555 if (!webgl->EnsureDefaultFB()) {
556 MOZ_ASSERT(!webgl->mDefaultFB);
557 MOZ_ASSERT(webgl->IsContextLost());
558 failureId = "FEATURE_FAILURE_WEBGL_BACKBUFFER"_ns;
559 return Err("Initializing WebGL backbuffer failed.");
562 return webgl;
563 }();
564 if (res.isOk()) {
565 failureId = "SUCCESS"_ns;
567 Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_FAILURE_ID, failureId);
569 if (!res.isOk()) {
570 out->error = res.unwrapErr();
571 return nullptr;
573 const auto webgl = res.unwrap();
575 // Update our internal stuff:
576 webgl->FinishInit();
578 reporter.SetSuccessful();
579 if (gl::GLContext::ShouldSpew()) {
580 printf_stderr("--- WebGL context created: %p\n", webgl.get());
583 // -
585 const auto UploadableSdTypes = [&]() {
586 webgl::EnumMask<layers::SurfaceDescriptor::Type> types;
587 if (webgl->gl->IsANGLE()) {
588 types[layers::SurfaceDescriptor::TSurfaceDescriptorD3D10] = true;
589 types[layers::SurfaceDescriptor::TSurfaceDescriptorDXGIYCbCr] = true;
591 if (kIsMacOS) {
592 types[layers::SurfaceDescriptor::TSurfaceDescriptorMacIOSurface] = true;
594 return types;
597 // -
599 out->options = webgl->mOptions;
600 out->limits = *webgl->mLimits;
601 out->uploadableSdTypes = UploadableSdTypes();
603 return webgl;
606 void WebGLContext::FinishInit() {
607 mOptions.antialias &= bool(mDefaultFB->mSamples);
609 if (!mOptions.alpha) {
610 // We always have alpha.
611 mNeedsFakeNoAlpha = true;
614 if (mOptions.depth || mOptions.stencil) {
615 // We always have depth+stencil if we have either.
616 if (!mOptions.depth) {
617 mNeedsFakeNoDepth = true;
619 if (!mOptions.stencil) {
620 mNeedsFakeNoStencil = true;
624 mNeedsFakeNoStencil_UserFBs = false;
625 #ifdef MOZ_WIDGET_COCOA
626 if (!nsCocoaFeatures::IsAtLeastVersion(10, 12) &&
627 gl->Vendor() == gl::GLVendor::Intel) {
628 mNeedsFakeNoStencil_UserFBs = true;
630 #endif
632 mResetLayer = true;
633 mOptionsFrozen = true;
635 //////
636 // Initial setup.
638 gl->mImplicitMakeCurrent = true;
639 gl->mElideDuplicateBindFramebuffers = true;
641 const auto& size = mDefaultFB->mSize;
643 mViewportX = mViewportY = 0;
644 mViewportWidth = size.width;
645 mViewportHeight = size.height;
646 gl->fViewport(mViewportX, mViewportY, mViewportWidth, mViewportHeight);
648 mScissorRect = {0, 0, size.width, size.height};
649 mScissorRect.Apply(*gl);
651 //////
652 // Check everything
654 AssertCachedBindings();
655 AssertCachedGlobalState();
657 mShouldPresent = true;
659 //////
661 gl->ResetSyncCallCount("WebGLContext Initialization");
662 LoseLruContextIfLimitExceeded();
665 void WebGLContext::SetCompositableHost(
666 RefPtr<layers::CompositableHost>& aCompositableHost) {
667 mCompositableHost = aCompositableHost;
670 void WebGLContext::LoseLruContextIfLimitExceeded() {
671 const auto maxContexts = std::max(1u, StaticPrefs::webgl_max_contexts());
672 const auto maxContextsPerPrincipal =
673 std::max(1u, StaticPrefs::webgl_max_contexts_per_principal());
675 // it's important to update the index on a new context before losing old
676 // contexts, otherwise new unused contexts would all have index 0 and we
677 // couldn't distinguish older ones when choosing which one to lose first.
678 BumpLru();
681 size_t forPrincipal = 0;
682 for (const auto& context : sWebglLru) {
683 if (context->mPrincipalKey == mPrincipalKey) {
684 forPrincipal += 1;
688 while (forPrincipal > maxContextsPerPrincipal) {
689 const auto text = nsPrintfCString(
690 "Exceeded %u live WebGL contexts for this principal, losing the "
691 "least recently used one.",
692 maxContextsPerPrincipal);
693 mHost->JsWarning(ToString(text));
695 for (const auto& context : sWebglLru) {
696 if (context->mPrincipalKey == mPrincipalKey) {
697 MOZ_ASSERT(context != this);
698 context->LoseContext(webgl::ContextLossReason::None);
699 forPrincipal -= 1;
700 break;
706 auto total = sWebglLru.size();
707 while (total > maxContexts) {
708 const auto text = nsPrintfCString(
709 "Exceeded %u live WebGL contexts, losing the least "
710 "recently used one.",
711 maxContexts);
712 mHost->JsWarning(ToString(text));
714 const auto& context = sWebglLru.front();
715 MOZ_ASSERT(context != this);
716 context->LoseContext(webgl::ContextLossReason::None);
717 total -= 1;
721 // -
723 namespace webgl {
725 ScopedPrepForResourceClear::ScopedPrepForResourceClear(
726 const WebGLContext& webgl_)
727 : webgl(webgl_) {
728 const auto& gl = webgl.gl;
730 if (webgl.mScissorTestEnabled) {
731 gl->fDisable(LOCAL_GL_SCISSOR_TEST);
733 if (webgl.mRasterizerDiscardEnabled) {
734 gl->fDisable(LOCAL_GL_RASTERIZER_DISCARD);
737 // "The clear operation always uses the front stencil write mask
738 // when clearing the stencil buffer."
739 webgl.DoColorMask(0x0f);
740 gl->fDepthMask(true);
741 gl->fStencilMaskSeparate(LOCAL_GL_FRONT, 0xffffffff);
743 gl->fClearColor(0.0f, 0.0f, 0.0f, 0.0f);
744 gl->fClearDepth(1.0f); // Depth formats are always cleared to 1.0f, not 0.0f.
745 gl->fClearStencil(0);
748 ScopedPrepForResourceClear::~ScopedPrepForResourceClear() {
749 const auto& gl = webgl.gl;
751 if (webgl.mScissorTestEnabled) {
752 gl->fEnable(LOCAL_GL_SCISSOR_TEST);
754 if (webgl.mRasterizerDiscardEnabled) {
755 gl->fEnable(LOCAL_GL_RASTERIZER_DISCARD);
758 // DoColorMask() is lazy.
759 gl->fDepthMask(webgl.mDepthWriteMask);
760 gl->fStencilMaskSeparate(LOCAL_GL_FRONT, webgl.mStencilWriteMaskFront);
762 gl->fClearColor(webgl.mColorClearValue[0], webgl.mColorClearValue[1],
763 webgl.mColorClearValue[2], webgl.mColorClearValue[3]);
764 gl->fClearDepth(webgl.mDepthClearValue);
765 gl->fClearStencil(webgl.mStencilClearValue);
768 } // namespace webgl
770 // -
772 void WebGLContext::OnEndOfFrame() {
773 if (StaticPrefs::webgl_perf_spew_frame_allocs()) {
774 GeneratePerfWarning("[webgl.perf.spew-frame-allocs] %" PRIu64
775 " data allocations this frame.",
776 mDataAllocGLCallCount);
778 mDataAllocGLCallCount = 0;
779 gl->ResetSyncCallCount("WebGLContext PresentScreenBuffer");
781 mDrawCallsSinceLastFlush = 0;
783 BumpLru();
786 void WebGLContext::BlitBackbufferToCurDriverFB(
787 const gl::MozFramebuffer* const source) const {
788 DoColorMask(0x0f);
790 if (mScissorTestEnabled) {
791 gl->fDisable(LOCAL_GL_SCISSOR_TEST);
794 [&]() {
795 const auto fb = source ? source : mDefaultFB.get();
797 if (gl->IsSupported(gl::GLFeature::framebuffer_blit)) {
798 gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, fb->mFB);
799 gl->fBlitFramebuffer(0, 0, fb->mSize.width, fb->mSize.height, 0, 0,
800 fb->mSize.width, fb->mSize.height,
801 LOCAL_GL_COLOR_BUFFER_BIT, LOCAL_GL_NEAREST);
802 return;
804 if (mDefaultFB->mSamples &&
805 gl->IsExtensionSupported(
806 gl::GLContext::APPLE_framebuffer_multisample)) {
807 gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, fb->mFB);
808 gl->fResolveMultisampleFramebufferAPPLE();
809 return;
812 gl->BlitHelper()->DrawBlitTextureToFramebuffer(fb->ColorTex(), fb->mSize,
813 fb->mSize);
814 }();
816 if (mScissorTestEnabled) {
817 gl->fEnable(LOCAL_GL_SCISSOR_TEST);
821 // -
823 template <typename T, typename... Args>
824 constexpr auto MakeArray(Args... args) -> std::array<T, sizeof...(Args)> {
825 return {{static_cast<T>(args)...}};
828 // -
830 // For an overview of how WebGL compositing works, see:
831 // https://wiki.mozilla.org/Platform/GFX/WebGL/Compositing
832 bool WebGLContext::PresentInto(gl::SwapChain& swapChain) {
833 OnEndOfFrame();
835 if (!ValidateAndInitFB(nullptr)) return false;
838 auto presenter = swapChain.Acquire(mDefaultFB->mSize);
839 if (!presenter) {
840 GenerateWarning("Swap chain surface creation failed.");
841 LoseContext();
842 return false;
845 const auto destFb = presenter->Fb();
846 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, destFb);
848 BlitBackbufferToCurDriverFB();
850 if (!mOptions.preserveDrawingBuffer) {
851 if (gl->IsSupported(gl::GLFeature::invalidate_framebuffer)) {
852 gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, mDefaultFB->mFB);
853 constexpr auto attachments = MakeArray<GLenum>(
854 LOCAL_GL_COLOR_ATTACHMENT0, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT);
855 gl->fInvalidateFramebuffer(LOCAL_GL_READ_FRAMEBUFFER,
856 attachments.size(), attachments.data());
858 mDefaultFB_IsInvalid = true;
861 #ifdef DEBUG
862 if (!mOptions.alpha) {
863 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, destFb);
864 uint32_t pixel = 0xffbadbad;
865 gl->fReadPixels(0, 0, 1, 1, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE,
866 &pixel);
867 MOZ_ASSERT((pixel & 0xff000000) == 0xff000000);
869 #endif
872 return true;
875 bool WebGLContext::PresentIntoXR(gl::SwapChain& swapChain,
876 const gl::MozFramebuffer& fb) {
877 OnEndOfFrame();
879 auto presenter = swapChain.Acquire(fb.mSize);
880 if (!presenter) {
881 GenerateWarning("Swap chain surface creation failed.");
882 LoseContext();
883 return false;
886 const auto destFb = presenter->Fb();
887 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, destFb);
889 BlitBackbufferToCurDriverFB(&fb);
891 // https://immersive-web.github.io/webxr/#opaque-framebuffer
892 // Opaque framebuffers will always be cleared regardless of the
893 // associated WebGL context’s preserveDrawingBuffer value.
894 if (gl->IsSupported(gl::GLFeature::invalidate_framebuffer)) {
895 gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, fb.mFB);
896 constexpr auto attachments = MakeArray<GLenum>(
897 LOCAL_GL_COLOR_ATTACHMENT0, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT);
898 gl->fInvalidateFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, attachments.size(),
899 attachments.data());
902 return true;
905 void WebGLContext::Present(WebGLFramebuffer* const xrFb,
906 const layers::TextureType consumerType,
907 const bool webvr) {
908 const FuncScope funcScope(*this, "<Present>");
909 if (IsContextLost()) return;
911 auto swapChain = webvr ? &mWebVRSwapChain : &mSwapChain;
912 if (xrFb) {
913 swapChain = &xrFb->mOpaqueSwapChain;
915 const gl::MozFramebuffer* maybeFB = nullptr;
916 if (xrFb) {
917 swapChain = &xrFb->mOpaqueSwapChain;
918 maybeFB = xrFb->mOpaque.get();
919 } else {
920 mResolvedDefaultFB = nullptr;
923 if (!swapChain->mFactory) {
924 auto typedFactory = gl::SurfaceFactory::Create(gl, consumerType);
925 if (typedFactory) {
926 swapChain->mFactory = std::move(typedFactory);
929 if (!swapChain->mFactory) {
930 NS_WARNING("Failed to make an ideal SurfaceFactory.");
931 swapChain->mFactory = MakeUnique<gl::SurfaceFactory_Basic>(*gl);
933 MOZ_ASSERT(swapChain->mFactory);
935 if (maybeFB) {
936 (void)PresentIntoXR(*swapChain, *maybeFB);
937 } else {
938 (void)PresentInto(*swapChain);
942 Maybe<layers::SurfaceDescriptor> WebGLContext::GetFrontBuffer(
943 WebGLFramebuffer* const xrFb, const bool webvr) {
944 auto swapChain = webvr ? &mWebVRSwapChain : &mSwapChain;
945 if (xrFb) {
946 swapChain = &xrFb->mOpaqueSwapChain;
948 const auto& front = swapChain->FrontBuffer();
949 if (!front) return {};
951 return front->ToSurfaceDescriptor();
954 Maybe<uvec2> WebGLContext::FrontBufferSnapshotInto(
955 const Maybe<Range<uint8_t>> maybeDest) {
956 const auto& front = mSwapChain.FrontBuffer();
957 if (!front) return {};
958 const auto& size = front->mDesc.size;
959 const auto ret = Some(*uvec2::FromSize(size));
960 if (!maybeDest) return ret;
961 const auto& dest = *maybeDest;
963 // -
965 front->WaitForBufferOwnership();
966 front->LockProd();
967 front->ProducerReadAcquire();
968 auto reset = MakeScopeExit([&] {
969 front->ProducerReadRelease();
970 front->UnlockProd();
973 // -
975 gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, 1);
976 if (IsWebGL2()) {
977 gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, 0);
978 gl->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, 0);
979 gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, 0);
982 // -
984 const auto readFbWas = mBoundReadFramebuffer;
985 const auto pboWas = mBoundPixelPackBuffer;
987 GLenum fbTarget = LOCAL_GL_READ_FRAMEBUFFER;
988 if (!IsWebGL2()) {
989 fbTarget = LOCAL_GL_FRAMEBUFFER;
991 auto reset2 = MakeScopeExit([&] {
992 DoBindFB(readFbWas, fbTarget);
993 if (pboWas) {
994 BindBuffer(LOCAL_GL_PIXEL_PACK_BUFFER, pboWas);
998 if (front->mFb) {
999 gl->fBindFramebuffer(fbTarget, front->mFb->mFB);
1000 } else {
1001 if (!BindDefaultFBForRead()) {
1002 gfxCriticalError() << "BindDefaultFBForRead failed";
1003 return {};
1006 if (pboWas) {
1007 BindBuffer(LOCAL_GL_PIXEL_PACK_BUFFER, nullptr);
1010 // -
1012 const size_t stride = size.width * 4;
1013 const size_t srcByteCount = stride * size.height;
1014 const auto dstByteCount = dest.length();
1015 if (srcByteCount != dstByteCount) {
1016 gfxCriticalError() << "FrontBufferSnapshotInto: srcByteCount:"
1017 << srcByteCount << " != dstByteCount:" << dstByteCount;
1018 return {};
1020 gl->fReadPixels(0, 0, size.width, size.height, LOCAL_GL_RGBA,
1021 LOCAL_GL_UNSIGNED_BYTE, dest.begin().get());
1022 gfxUtils::ConvertBGRAtoRGBA(dest.begin().get(), dstByteCount);
1024 return ret;
1027 void WebGLContext::ClearVRSwapChain() { mWebVRSwapChain.ClearPool(); }
1029 // ------------------------
1031 RefPtr<gfx::DataSourceSurface> GetTempSurface(const gfx::IntSize& aSize,
1032 gfx::SurfaceFormat& aFormat) {
1033 uint32_t stride =
1034 gfx::GetAlignedStride<8>(aSize.width, BytesPerPixel(aFormat));
1035 return gfx::Factory::CreateDataSourceSurfaceWithStride(aSize, aFormat,
1036 stride);
1039 void WebGLContext::DummyReadFramebufferOperation() {
1040 if (!mBoundReadFramebuffer) return; // Infallible.
1042 const auto status = mBoundReadFramebuffer->CheckFramebufferStatus();
1043 if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
1044 ErrorInvalidFramebufferOperation("Framebuffer must be complete.");
1048 bool WebGLContext::Has64BitTimestamps() const {
1049 // 'sync' provides glGetInteger64v either by supporting ARB_sync, GL3+, or
1050 // GLES3+.
1051 return gl->IsSupported(gl::GLFeature::sync);
1054 static bool CheckContextLost(gl::GLContext* gl, bool* const out_isGuilty) {
1055 MOZ_ASSERT(gl);
1057 const auto resetStatus = gl->fGetGraphicsResetStatus();
1058 if (resetStatus == LOCAL_GL_NO_ERROR) {
1059 *out_isGuilty = false;
1060 return false;
1063 // Assume guilty unless we find otherwise!
1064 bool isGuilty = true;
1065 switch (resetStatus) {
1066 case LOCAL_GL_INNOCENT_CONTEXT_RESET_ARB:
1067 case LOCAL_GL_PURGED_CONTEXT_RESET_NV:
1068 // Either nothing wrong, or not our fault.
1069 isGuilty = false;
1070 break;
1071 case LOCAL_GL_GUILTY_CONTEXT_RESET_ARB:
1072 NS_WARNING(
1073 "WebGL content on the page definitely caused the graphics"
1074 " card to reset.");
1075 break;
1076 case LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB:
1077 NS_WARNING(
1078 "WebGL content on the page might have caused the graphics"
1079 " card to reset");
1080 // If we can't tell, assume not-guilty.
1081 // Todo: Implement max number of "unknown" resets per document or time.
1082 isGuilty = false;
1083 break;
1084 default:
1085 gfxCriticalError() << "Unexpected glGetGraphicsResetStatus: "
1086 << gfx::hexa(resetStatus);
1087 break;
1090 if (isGuilty) {
1091 NS_WARNING(
1092 "WebGL context on this page is considered guilty, and will"
1093 " not be restored.");
1096 *out_isGuilty = isGuilty;
1097 return true;
1100 void WebGLContext::RunContextLossTimer() { mContextLossHandler.RunTimer(); }
1102 // We use this timer for many things. Here are the things that it is activated
1103 // for:
1104 // 1) If a script is using the MOZ_WEBGL_lose_context extension.
1105 // 2) If we are using EGL and _NOT ANGLE_, we query periodically to see if the
1106 // CONTEXT_LOST_WEBGL error has been triggered.
1107 // 3) If we are using ANGLE, or anything that supports ARB_robustness, query the
1108 // GPU periodically to see if the reset status bit has been set.
1109 // In all of these situations, we use this timer to send the script context lost
1110 // and restored events asynchronously. For example, if it triggers a context
1111 // loss, the webglcontextlost event will be sent to it the next time the
1112 // robustness timer fires.
1113 // Note that this timer mechanism is not used unless one of these 3 criteria are
1114 // met.
1115 // At a bare minimum, from context lost to context restores, it would take 3
1116 // full timer iterations: detection, webglcontextlost, webglcontextrestored.
1117 void WebGLContext::CheckForContextLoss() {
1118 bool isGuilty = true;
1119 const auto isContextLost = CheckContextLost(gl, &isGuilty);
1120 if (!isContextLost) return;
1122 mWebGLError = LOCAL_GL_CONTEXT_LOST;
1124 auto reason = webgl::ContextLossReason::None;
1125 if (isGuilty) {
1126 reason = webgl::ContextLossReason::Guilty;
1128 LoseContext(reason);
1131 void WebGLContext::LoseContext(const webgl::ContextLossReason reason) {
1132 printf_stderr("WebGL(%p)::LoseContext(%u)\n", this,
1133 static_cast<uint32_t>(reason));
1134 mIsContextLost = true;
1135 mLruPosition = {};
1136 mHost->OnContextLoss(reason);
1139 void WebGLContext::DidRefresh() {
1140 if (gl) {
1141 gl->FlushIfHeavyGLCallsSinceLastFlush();
1145 ////////////////////////////////////////////////////////////////////////////////
1147 uvec2 WebGLContext::DrawingBufferSize() {
1148 const FuncScope funcScope(*this, "width/height");
1149 if (IsContextLost()) return {};
1151 if (!EnsureDefaultFB()) return {};
1153 return *uvec2::FromSize(mDefaultFB->mSize);
1156 bool WebGLContext::ValidateAndInitFB(const WebGLFramebuffer* const fb,
1157 const GLenum incompleteFbError) {
1158 if (fb) return fb->ValidateAndInitAttachments(incompleteFbError);
1160 if (!EnsureDefaultFB()) return false;
1162 if (mDefaultFB_IsInvalid) {
1163 // Clear it!
1164 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mDefaultFB->mFB);
1165 const webgl::ScopedPrepForResourceClear scopedPrep(*this);
1166 if (!mOptions.alpha) {
1167 gl->fClearColor(0, 0, 0, 1);
1169 const GLbitfield bits = LOCAL_GL_COLOR_BUFFER_BIT |
1170 LOCAL_GL_DEPTH_BUFFER_BIT |
1171 LOCAL_GL_STENCIL_BUFFER_BIT;
1172 gl->fClear(bits);
1174 mDefaultFB_IsInvalid = false;
1176 return true;
1179 void WebGLContext::DoBindFB(const WebGLFramebuffer* const fb,
1180 const GLenum target) const {
1181 const GLenum driverFB = fb ? fb->mGLName : mDefaultFB->mFB;
1182 gl->fBindFramebuffer(target, driverFB);
1185 bool WebGLContext::BindCurFBForDraw() {
1186 const auto& fb = mBoundDrawFramebuffer;
1187 if (!ValidateAndInitFB(fb)) return false;
1189 DoBindFB(fb);
1190 return true;
1193 bool WebGLContext::BindCurFBForColorRead(
1194 const webgl::FormatUsageInfo** const out_format, uint32_t* const out_width,
1195 uint32_t* const out_height, const GLenum incompleteFbError) {
1196 const auto& fb = mBoundReadFramebuffer;
1198 if (fb) {
1199 if (!ValidateAndInitFB(fb, incompleteFbError)) return false;
1200 if (!fb->ValidateForColorRead(out_format, out_width, out_height))
1201 return false;
1203 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fb->mGLName);
1204 return true;
1207 if (!BindDefaultFBForRead()) return false;
1209 if (mDefaultFB_ReadBuffer == LOCAL_GL_NONE) {
1210 ErrorInvalidOperation(
1211 "Can't read from backbuffer when readBuffer mode is NONE.");
1212 return false;
1215 auto effFormat = mOptions.alpha ? webgl::EffectiveFormat::RGBA8
1216 : webgl::EffectiveFormat::RGB8;
1218 *out_format = mFormatUsage->GetUsage(effFormat);
1219 MOZ_ASSERT(*out_format);
1221 *out_width = mDefaultFB->mSize.width;
1222 *out_height = mDefaultFB->mSize.height;
1223 return true;
1226 bool WebGLContext::BindDefaultFBForRead() {
1227 if (!ValidateAndInitFB(nullptr)) return false;
1229 if (!mDefaultFB->mSamples) {
1230 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mDefaultFB->mFB);
1231 return true;
1234 if (!mResolvedDefaultFB) {
1235 mResolvedDefaultFB =
1236 gl::MozFramebuffer::Create(gl, mDefaultFB->mSize, 0, false);
1237 if (!mResolvedDefaultFB) {
1238 gfxCriticalNote << FuncName() << ": Failed to create mResolvedDefaultFB.";
1239 return false;
1243 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mResolvedDefaultFB->mFB);
1244 BlitBackbufferToCurDriverFB();
1246 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mResolvedDefaultFB->mFB);
1247 return true;
1250 void WebGLContext::DoColorMask(const uint8_t bitmask) const {
1251 if (mDriverColorMask0 != bitmask) {
1252 mDriverColorMask0 = bitmask;
1253 const auto bs = std::bitset<4>(bitmask);
1254 if (gl->IsSupported(gl::GLFeature::draw_buffers_indexed)) {
1255 gl->fColorMaski(0, bs[0], bs[1], bs[2], bs[3]);
1256 } else {
1257 gl->fColorMask(bs[0], bs[1], bs[2], bs[3]);
1262 ////////////////////////////////////////////////////////////////////////////////
1264 ScopedDrawCallWrapper::ScopedDrawCallWrapper(WebGLContext& webgl)
1265 : mWebGL(webgl) {
1266 uint8_t driverColorMask0 = mWebGL.mColorWriteMask0;
1267 bool driverDepthTest = mWebGL.mDepthTestEnabled;
1268 bool driverStencilTest = mWebGL.mStencilTestEnabled;
1269 const auto& fb = mWebGL.mBoundDrawFramebuffer;
1270 if (!fb) {
1271 if (mWebGL.mDefaultFB_DrawBuffer0 == LOCAL_GL_NONE) {
1272 driverColorMask0 = 0; // Is this well-optimized enough for depth-first
1273 // rendering?
1274 } else {
1275 driverColorMask0 &= ~(uint8_t(mWebGL.mNeedsFakeNoAlpha) << 3);
1277 driverDepthTest &= !mWebGL.mNeedsFakeNoDepth;
1278 driverStencilTest &= !mWebGL.mNeedsFakeNoStencil;
1279 } else {
1280 if (mWebGL.mNeedsFakeNoStencil_UserFBs &&
1281 fb->DepthAttachment().HasAttachment() &&
1282 !fb->StencilAttachment().HasAttachment()) {
1283 driverStencilTest = false;
1287 const auto& gl = mWebGL.gl;
1288 mWebGL.DoColorMask(driverColorMask0);
1289 if (mWebGL.mDriverDepthTest != driverDepthTest) {
1290 // "When disabled, the depth comparison and subsequent possible updates to
1291 // the
1292 // depth buffer value are bypassed and the fragment is passed to the next
1293 // operation." [GLES 3.0.5, p177]
1294 mWebGL.mDriverDepthTest = driverDepthTest;
1295 gl->SetEnabled(LOCAL_GL_DEPTH_TEST, mWebGL.mDriverDepthTest);
1297 if (mWebGL.mDriverStencilTest != driverStencilTest) {
1298 // "When disabled, the stencil test and associated modifications are not
1299 // made, and
1300 // the fragment is always passed." [GLES 3.0.5, p175]
1301 mWebGL.mDriverStencilTest = driverStencilTest;
1302 gl->SetEnabled(LOCAL_GL_STENCIL_TEST, mWebGL.mDriverStencilTest);
1306 ScopedDrawCallWrapper::~ScopedDrawCallWrapper() {
1307 if (mWebGL.mBoundDrawFramebuffer) return;
1309 mWebGL.mResolvedDefaultFB = nullptr;
1310 mWebGL.mShouldPresent = true;
1313 // -
1315 void WebGLContext::ScissorRect::Apply(gl::GLContext& gl) const {
1316 gl.fScissor(x, y, w, h);
1319 ////////////////////////////////////////
1321 IndexedBufferBinding::IndexedBufferBinding() : mRangeStart(0), mRangeSize(0) {}
1323 uint64_t IndexedBufferBinding::ByteCount() const {
1324 if (!mBufferBinding) return 0;
1326 uint64_t bufferSize = mBufferBinding->ByteLength();
1327 if (!mRangeSize) // BindBufferBase
1328 return bufferSize;
1330 if (mRangeStart >= bufferSize) return 0;
1331 bufferSize -= mRangeStart;
1333 return std::min(bufferSize, mRangeSize);
1336 ////////////////////////////////////////
1338 ScopedFBRebinder::~ScopedFBRebinder() {
1339 const auto fnName = [&](WebGLFramebuffer* fb) {
1340 return fb ? fb->mGLName : 0;
1343 const auto& gl = mWebGL->gl;
1344 if (mWebGL->IsWebGL2()) {
1345 gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER,
1346 fnName(mWebGL->mBoundDrawFramebuffer));
1347 gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER,
1348 fnName(mWebGL->mBoundReadFramebuffer));
1349 } else {
1350 MOZ_ASSERT(mWebGL->mBoundDrawFramebuffer == mWebGL->mBoundReadFramebuffer);
1351 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER,
1352 fnName(mWebGL->mBoundDrawFramebuffer));
1356 ////////////////////
1358 void DoBindBuffer(gl::GLContext& gl, const GLenum target,
1359 const WebGLBuffer* const buffer) {
1360 gl.fBindBuffer(target, buffer ? buffer->mGLName : 0);
1363 ////////////////////////////////////////
1365 bool Intersect(const int32_t srcSize, const int32_t read0,
1366 const int32_t readSize, int32_t* const out_intRead0,
1367 int32_t* const out_intWrite0, int32_t* const out_intSize) {
1368 MOZ_ASSERT(srcSize >= 0);
1369 MOZ_ASSERT(readSize >= 0);
1370 const auto read1 = int64_t(read0) + readSize;
1372 int32_t intRead0 = read0; // Clearly doesn't need validation.
1373 int64_t intWrite0 = 0;
1374 int64_t intSize = readSize;
1376 if (read1 <= 0 || read0 >= srcSize) {
1377 // Disjoint ranges.
1378 intSize = 0;
1379 } else {
1380 if (read0 < 0) {
1381 const auto diff = int64_t(0) - read0;
1382 MOZ_ASSERT(diff >= 0);
1383 intRead0 = 0;
1384 intWrite0 = diff;
1385 intSize -= diff;
1387 if (read1 > srcSize) {
1388 const auto diff = int64_t(read1) - srcSize;
1389 MOZ_ASSERT(diff >= 0);
1390 intSize -= diff;
1393 if (!CheckedInt<int32_t>(intWrite0).isValid() ||
1394 !CheckedInt<int32_t>(intSize).isValid()) {
1395 return false;
1399 *out_intRead0 = intRead0;
1400 *out_intWrite0 = intWrite0;
1401 *out_intSize = intSize;
1402 return true;
1405 // --
1407 uint64_t AvailGroups(const uint64_t totalAvailItems,
1408 const uint64_t firstItemOffset, const uint32_t groupSize,
1409 const uint32_t groupStride) {
1410 MOZ_ASSERT(groupSize && groupStride);
1411 MOZ_ASSERT(groupSize <= groupStride);
1413 if (totalAvailItems <= firstItemOffset) return 0;
1414 const size_t availItems = totalAvailItems - firstItemOffset;
1416 size_t availGroups = availItems / groupStride;
1417 const size_t tailItems = availItems % groupStride;
1418 if (tailItems >= groupSize) {
1419 availGroups += 1;
1421 return availGroups;
1424 ////////////////////////////////////////////////////////////////////////////////
1426 const char* WebGLContext::FuncName() const {
1427 const char* ret;
1428 if (MOZ_LIKELY(mFuncScope)) {
1429 ret = mFuncScope->mFuncName;
1430 } else {
1431 NS_WARNING("FuncScope not on stack!");
1432 ret = "<unknown function>";
1434 return ret;
1437 // -
1439 WebGLContext::FuncScope::FuncScope(const WebGLContext& webgl,
1440 const char* const funcName)
1441 : mWebGL(webgl), mFuncName(bool(mWebGL.mFuncScope) ? nullptr : funcName) {
1442 if (!mFuncName) return;
1443 mWebGL.mFuncScope = this;
1446 WebGLContext::FuncScope::~FuncScope() {
1447 if (mBindFailureGuard) {
1448 gfxCriticalError() << "mBindFailureGuard failure: Early exit from "
1449 << mWebGL.FuncName();
1452 if (!mFuncName) return;
1453 mWebGL.mFuncScope = nullptr;
1456 // --
1458 bool ClientWebGLContext::IsXRCompatible() const { return mXRCompatible; }
1460 already_AddRefed<dom::Promise> ClientWebGLContext::MakeXRCompatible(
1461 ErrorResult& aRv) {
1462 const FuncScope funcScope(*this, "MakeXRCompatible");
1463 nsCOMPtr<nsIGlobalObject> global;
1464 // TODO: Bug 1596921
1465 // Should use nsICanvasRenderingContextInternal::GetParentObject
1466 // once it has been updated to work in the offscreencanvas case
1467 if (mCanvasElement) {
1468 global = GetOwnerDoc()->GetScopeObject();
1469 } else if (mOffscreenCanvas) {
1470 global = mOffscreenCanvas->GetOwnerGlobal();
1472 if (!global) {
1473 aRv.ThrowInvalidAccessError(
1474 "Using a WebGL context that is not attached to either a canvas or an "
1475 "OffscreenCanvas");
1476 return nullptr;
1478 RefPtr<dom::Promise> promise = dom::Promise::Create(global, aRv);
1479 NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
1481 if (IsContextLost()) {
1482 promise->MaybeRejectWithInvalidStateError(
1483 "Can not make context XR compatible when context is already lost.");
1484 return promise.forget();
1487 // TODO: Bug 1580258 - WebGLContext.MakeXRCompatible needs to switch to
1488 // the device connected to the XR hardware
1489 // This should update `options` and lose+restore the context.
1490 mXRCompatible = true;
1491 promise->MaybeResolveWithUndefined();
1492 return promise.forget();
1495 // --
1497 webgl::AvailabilityRunnable& ClientWebGLContext::EnsureAvailabilityRunnable()
1498 const {
1499 if (!mAvailabilityRunnable) {
1500 mAvailabilityRunnable = new webgl::AvailabilityRunnable(this);
1501 auto forgettable = mAvailabilityRunnable;
1502 NS_DispatchToCurrentThread(forgettable.forget());
1504 return *mAvailabilityRunnable;
1507 webgl::AvailabilityRunnable::AvailabilityRunnable(
1508 const ClientWebGLContext* const webgl)
1509 : Runnable("webgl::AvailabilityRunnable"), mWebGL(webgl) {}
1511 webgl::AvailabilityRunnable::~AvailabilityRunnable() {
1512 MOZ_ASSERT(mQueries.empty());
1513 MOZ_ASSERT(mSyncs.empty());
1516 nsresult webgl::AvailabilityRunnable::Run() {
1517 for (const auto& cur : mQueries) {
1518 if (!cur) continue;
1519 cur->mCanBeAvailable = true;
1521 mQueries.clear();
1523 for (const auto& cur : mSyncs) {
1524 if (!cur) continue;
1525 cur->mCanBeAvailable = true;
1527 mSyncs.clear();
1529 if (mWebGL) {
1530 mWebGL->mAvailabilityRunnable = nullptr;
1532 return NS_OK;
1535 // -
1537 void WebGLContext::GenerateErrorImpl(const GLenum errOrWarning,
1538 const std::string& text) const {
1539 auto err = errOrWarning;
1540 bool isPerfWarning = false;
1541 if (err == webgl::kErrorPerfWarning) {
1542 err = 0;
1543 isPerfWarning = true;
1546 if (err && mFuncScope && mFuncScope->mBindFailureGuard) {
1547 gfxCriticalError() << "mBindFailureGuard failure: Generating error "
1548 << EnumString(err) << ": " << text;
1551 /* ES2 section 2.5 "GL Errors" states that implementations can have
1552 * multiple 'flags', as errors might be caught in different parts of
1553 * a distributed implementation.
1554 * We're signing up as a distributed implementation here, with
1555 * separate flags for WebGL and the underlying GLContext.
1557 if (!mWebGLError) mWebGLError = err;
1559 if (!mHost) return; // Impossible?
1561 // -
1563 const auto ShouldWarn = [&]() {
1564 if (isPerfWarning) {
1565 return ShouldGeneratePerfWarnings();
1567 return ShouldGenerateWarnings();
1569 if (!ShouldWarn()) return;
1571 // -
1573 auto* pNumWarnings = &mWarningCount;
1574 const char* warningsType = "warnings";
1575 if (isPerfWarning) {
1576 pNumWarnings = &mNumPerfWarnings;
1577 warningsType = "perf warnings";
1580 if (isPerfWarning) {
1581 const auto perfText = std::string("WebGL perf warning: ") + text;
1582 mHost->JsWarning(perfText);
1583 } else {
1584 mHost->JsWarning(text);
1586 *pNumWarnings += 1;
1588 if (!ShouldWarn()) {
1589 const auto& msg = nsPrintfCString(
1590 "After reporting %i, no further %s will be reported for this WebGL "
1591 "context.",
1592 int(*pNumWarnings), warningsType);
1593 mHost->JsWarning(ToString(msg));
1597 // -
1599 Maybe<std::string> WebGLContext::GetString(const GLenum pname) const {
1600 const WebGLContext::FuncScope funcScope(*this, "getParameter");
1601 if (IsContextLost()) return {};
1603 const auto FromRaw = [](const char* const raw) -> Maybe<std::string> {
1604 if (!raw) return {};
1605 return Some(std::string(raw));
1608 switch (pname) {
1609 case LOCAL_GL_EXTENSIONS: {
1610 if (!gl->IsCoreProfile()) {
1611 const auto rawExt = (const char*)gl->fGetString(LOCAL_GL_EXTENSIONS);
1612 return FromRaw(rawExt);
1614 std::string ret;
1615 const auto& numExts = gl->GetIntAs<GLuint>(LOCAL_GL_NUM_EXTENSIONS);
1616 for (GLuint i = 0; i < numExts; i++) {
1617 const auto rawExt =
1618 (const char*)gl->fGetStringi(LOCAL_GL_EXTENSIONS, i);
1619 if (!rawExt) continue;
1621 if (i > 0) {
1622 ret += " ";
1624 ret += rawExt;
1626 return Some(std::move(ret));
1629 case LOCAL_GL_RENDERER:
1630 case LOCAL_GL_VENDOR:
1631 case LOCAL_GL_VERSION: {
1632 const auto raw = (const char*)gl->fGetString(pname);
1633 return FromRaw(raw);
1636 case dom::MOZ_debug_Binding::WSI_INFO: {
1637 nsCString info;
1638 gl->GetWSIInfo(&info);
1639 return Some(std::string(info.BeginReading()));
1642 default:
1643 ErrorInvalidEnumArg("pname", pname);
1644 return {};
1648 // ---------------------------------
1650 Maybe<webgl::IndexedName> webgl::ParseIndexed(const std::string& str) {
1651 static const std::regex kRegex("(.*)\\[([0-9]+)\\]");
1653 std::smatch match;
1654 if (!std::regex_match(str, match, kRegex)) return {};
1656 const auto index = std::stoull(match[2]);
1657 return Some(webgl::IndexedName{match[1], index});
1660 // ExplodeName("foo.bar[3].x") -> ["foo", ".", "bar", "[", "3", "]", ".", "x"]
1661 static std::vector<std::string> ExplodeName(const std::string& str) {
1662 std::vector<std::string> ret;
1664 static const std::regex kSep("[.[\\]]");
1666 auto itr = std::regex_token_iterator<decltype(str.begin())>(
1667 str.begin(), str.end(), kSep, {-1, 0});
1668 const auto end = decltype(itr)();
1670 for (; itr != end; ++itr) {
1671 const auto& part = itr->str();
1672 if (part.size()) {
1673 ret.push_back(part);
1676 return ret;
1681 //#define DUMP_MakeLinkResult
1683 webgl::LinkActiveInfo GetLinkActiveInfo(
1684 gl::GLContext& gl, const GLuint prog, const bool webgl2,
1685 const std::unordered_map<std::string, std::string>& nameUnmap) {
1686 webgl::LinkActiveInfo ret;
1687 [&]() {
1688 const auto fnGetProgramui = [&](const GLenum pname) {
1689 GLint ret = 0;
1690 gl.fGetProgramiv(prog, pname, &ret);
1691 return static_cast<uint32_t>(ret);
1694 std::vector<char> stringBuffer(1);
1695 const auto fnEnsureCapacity = [&](const GLenum pname) {
1696 const auto maxWithNull = fnGetProgramui(pname);
1697 if (maxWithNull > stringBuffer.size()) {
1698 stringBuffer.resize(maxWithNull);
1702 fnEnsureCapacity(LOCAL_GL_ACTIVE_ATTRIBUTE_MAX_LENGTH);
1703 fnEnsureCapacity(LOCAL_GL_ACTIVE_UNIFORM_MAX_LENGTH);
1704 if (webgl2) {
1705 fnEnsureCapacity(LOCAL_GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH);
1706 fnEnsureCapacity(LOCAL_GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH);
1709 // -
1711 const auto fnUnmapName = [&](const std::string& mappedName) {
1712 const auto parts = ExplodeName(mappedName);
1714 std::ostringstream ret;
1715 for (const auto& part : parts) {
1716 const auto maybe = MaybeFind(nameUnmap, part);
1717 if (maybe) {
1718 ret << *maybe;
1719 } else {
1720 ret << part;
1723 return ret.str();
1726 // -
1729 const auto count = fnGetProgramui(LOCAL_GL_ACTIVE_ATTRIBUTES);
1730 ret.activeAttribs.reserve(count);
1731 for (const auto i : IntegerRange(count)) {
1732 GLsizei lengthWithoutNull = 0;
1733 GLint elemCount = 0; // `size`
1734 GLenum elemType = 0; // `type`
1735 gl.fGetActiveAttrib(prog, i, stringBuffer.size(), &lengthWithoutNull,
1736 &elemCount, &elemType, stringBuffer.data());
1737 if (!elemType) {
1738 const auto error = gl.fGetError();
1739 if (error != LOCAL_GL_CONTEXT_LOST) {
1740 gfxCriticalError() << "Failed to do glGetActiveAttrib: " << error;
1742 return;
1744 const auto mappedName =
1745 std::string(stringBuffer.data(), lengthWithoutNull);
1746 const auto userName = fnUnmapName(mappedName);
1748 auto loc = gl.fGetAttribLocation(prog, mappedName.c_str());
1749 if (mappedName.find("gl_") == 0) {
1750 // Bug 1328559: Appears problematic on ANGLE and OSX, but not Linux or
1751 // Win+GL.
1752 loc = -1;
1755 #ifdef DUMP_MakeLinkResult
1756 printf_stderr("[attrib %u/%u] @%i %s->%s\n", i, count, loc,
1757 userName.c_str(), mappedName.c_str());
1758 #endif
1759 webgl::ActiveAttribInfo info;
1760 info.elemType = elemType;
1761 info.elemCount = elemCount;
1762 info.name = userName;
1763 info.location = loc;
1764 info.baseType = webgl::ToAttribBaseType(info.elemType);
1765 ret.activeAttribs.push_back(std::move(info));
1769 // -
1772 const auto count = fnGetProgramui(LOCAL_GL_ACTIVE_UNIFORMS);
1773 ret.activeUniforms.reserve(count);
1775 std::vector<GLint> blockIndexList(count, -1);
1776 std::vector<GLint> blockOffsetList(count, -1);
1777 std::vector<GLint> blockArrayStrideList(count, -1);
1778 std::vector<GLint> blockMatrixStrideList(count, -1);
1779 std::vector<GLint> blockIsRowMajorList(count, 0);
1781 if (webgl2 && count) {
1782 std::vector<GLuint> activeIndices;
1783 activeIndices.reserve(count);
1784 for (const auto i : IntegerRange(count)) {
1785 activeIndices.push_back(i);
1788 gl.fGetActiveUniformsiv(
1789 prog, activeIndices.size(), activeIndices.data(),
1790 LOCAL_GL_UNIFORM_BLOCK_INDEX, blockIndexList.data());
1792 gl.fGetActiveUniformsiv(prog, activeIndices.size(),
1793 activeIndices.data(), LOCAL_GL_UNIFORM_OFFSET,
1794 blockOffsetList.data());
1796 gl.fGetActiveUniformsiv(
1797 prog, activeIndices.size(), activeIndices.data(),
1798 LOCAL_GL_UNIFORM_ARRAY_STRIDE, blockArrayStrideList.data());
1800 gl.fGetActiveUniformsiv(
1801 prog, activeIndices.size(), activeIndices.data(),
1802 LOCAL_GL_UNIFORM_MATRIX_STRIDE, blockMatrixStrideList.data());
1804 gl.fGetActiveUniformsiv(
1805 prog, activeIndices.size(), activeIndices.data(),
1806 LOCAL_GL_UNIFORM_IS_ROW_MAJOR, blockIsRowMajorList.data());
1809 for (const auto i : IntegerRange(count)) {
1810 GLsizei lengthWithoutNull = 0;
1811 GLint elemCount = 0; // `size`
1812 GLenum elemType = 0; // `type`
1813 gl.fGetActiveUniform(prog, i, stringBuffer.size(), &lengthWithoutNull,
1814 &elemCount, &elemType, stringBuffer.data());
1815 if (!elemType) {
1816 const auto error = gl.fGetError();
1817 if (error != LOCAL_GL_CONTEXT_LOST) {
1818 gfxCriticalError() << "Failed to do glGetActiveUniform: " << error;
1820 return;
1822 auto mappedName = std::string(stringBuffer.data(), lengthWithoutNull);
1824 // Get true name
1826 auto baseMappedName = mappedName;
1828 const bool isArray = [&]() {
1829 const auto maybe = webgl::ParseIndexed(mappedName);
1830 if (maybe) {
1831 MOZ_ASSERT(maybe->index == 0);
1832 baseMappedName = std::move(maybe->name);
1833 return true;
1835 return false;
1836 }();
1838 const auto userName = fnUnmapName(mappedName);
1840 // -
1842 webgl::ActiveUniformInfo info;
1843 info.elemType = elemType;
1844 info.elemCount = static_cast<uint32_t>(elemCount);
1845 info.name = userName;
1846 info.block_index = blockIndexList[i];
1847 info.block_offset = blockOffsetList[i];
1848 info.block_arrayStride = blockArrayStrideList[i];
1849 info.block_matrixStride = blockMatrixStrideList[i];
1850 info.block_isRowMajor = bool(blockIsRowMajorList[i]);
1852 #ifdef DUMP_MakeLinkResult
1853 printf_stderr("[uniform %u/%u] %s->%s\n", i + 1, count,
1854 userName.c_str(), mappedName.c_str());
1855 #endif
1857 // Get uniform locations
1859 auto locName = baseMappedName;
1860 const auto baseLength = locName.size();
1861 for (const auto i : IntegerRange(info.elemCount)) {
1862 if (isArray) {
1863 locName.erase(
1864 baseLength); // Erase previous [N], but retain capacity.
1865 locName += '[';
1866 locName += std::to_string(i);
1867 locName += ']';
1869 const auto loc = gl.fGetUniformLocation(prog, locName.c_str());
1870 if (loc != -1) {
1871 info.locByIndex[i] = static_cast<uint32_t>(loc);
1872 #ifdef DUMP_MakeLinkResult
1873 printf_stderr(" [%u] @%i\n", i, loc);
1874 #endif
1877 } // anon
1879 ret.activeUniforms.push_back(std::move(info));
1880 } // for i
1881 } // anon
1883 if (webgl2) {
1884 // -------------------------------------
1885 // active uniform blocks
1887 const auto count = fnGetProgramui(LOCAL_GL_ACTIVE_UNIFORM_BLOCKS);
1888 ret.activeUniformBlocks.reserve(count);
1890 for (const auto i : IntegerRange(count)) {
1891 GLsizei lengthWithoutNull = 0;
1892 gl.fGetActiveUniformBlockName(prog, i, stringBuffer.size(),
1893 &lengthWithoutNull,
1894 stringBuffer.data());
1895 const auto mappedName =
1896 std::string(stringBuffer.data(), lengthWithoutNull);
1897 const auto userName = fnUnmapName(mappedName);
1899 // -
1901 auto info = webgl::ActiveUniformBlockInfo{userName};
1902 GLint val = 0;
1904 gl.fGetActiveUniformBlockiv(prog, i, LOCAL_GL_UNIFORM_BLOCK_DATA_SIZE,
1905 &val);
1906 info.dataSize = static_cast<uint32_t>(val);
1908 gl.fGetActiveUniformBlockiv(
1909 prog, i, LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &val);
1910 info.activeUniformIndices.resize(val);
1911 gl.fGetActiveUniformBlockiv(
1912 prog, i, LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES,
1913 reinterpret_cast<GLint*>(info.activeUniformIndices.data()));
1915 gl.fGetActiveUniformBlockiv(
1916 prog, i, LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER,
1917 &val);
1918 info.referencedByVertexShader = bool(val);
1920 gl.fGetActiveUniformBlockiv(
1921 prog, i, LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER,
1922 &val);
1923 info.referencedByFragmentShader = bool(val);
1925 ret.activeUniformBlocks.push_back(std::move(info));
1926 } // for i
1927 } // anon
1929 // -------------------------------------
1930 // active tf varyings
1932 const auto count = fnGetProgramui(LOCAL_GL_TRANSFORM_FEEDBACK_VARYINGS);
1933 ret.activeTfVaryings.reserve(count);
1935 for (const auto i : IntegerRange(count)) {
1936 GLsizei lengthWithoutNull = 0;
1937 GLsizei elemCount = 0; // `size`
1938 GLenum elemType = 0; // `type`
1939 gl.fGetTransformFeedbackVarying(prog, i, stringBuffer.size(),
1940 &lengthWithoutNull, &elemCount,
1941 &elemType, stringBuffer.data());
1942 const auto mappedName =
1943 std::string(stringBuffer.data(), lengthWithoutNull);
1944 const auto userName = fnUnmapName(mappedName);
1946 ret.activeTfVaryings.push_back(
1947 {elemType, static_cast<uint32_t>(elemCount), userName});
1950 } // if webgl2
1951 }();
1952 return ret;
1955 nsCString ToCString(const std::string& s) {
1956 return nsCString(s.data(), s.size());
1959 webgl::CompileResult WebGLContext::GetCompileResult(
1960 const WebGLShader& shader) const {
1961 webgl::CompileResult ret;
1962 [&]() {
1963 ret.pending = false;
1964 const auto& info = shader.CompileResults();
1965 if (!info) return;
1966 if (!info->mValid) {
1967 ret.log = info->mInfoLog.c_str();
1968 return;
1970 // TODO: These could be large and should be made fallible.
1971 ret.translatedSource = ToCString(info->mObjectCode);
1972 ret.log = ToCString(shader.CompileLog());
1973 if (!shader.IsCompiled()) return;
1974 ret.success = true;
1975 }();
1976 return ret;
1979 webgl::LinkResult WebGLContext::GetLinkResult(const WebGLProgram& prog) const {
1980 webgl::LinkResult ret;
1981 [&]() {
1982 ret.pending = false; // Link status polling not yet implemented.
1983 ret.log = ToCString(prog.LinkLog());
1984 const auto& info = prog.LinkInfo();
1985 if (!info) return;
1986 ret.success = true;
1987 ret.active = info->active;
1988 ret.tfBufferMode = info->transformFeedbackBufferMode;
1989 }();
1990 return ret;
1993 // -
1995 GLint WebGLContext::GetFragDataLocation(const WebGLProgram& prog,
1996 const std::string& userName) const {
1997 const auto err = CheckGLSLVariableName(IsWebGL2(), userName);
1998 if (err) {
1999 GenerateError(err->type, "%s", err->info.c_str());
2000 return -1;
2003 const auto& info = prog.LinkInfo();
2004 if (!info) return -1;
2005 const auto& nameMap = info->nameMap;
2007 const auto parts = ExplodeName(userName);
2009 std::ostringstream ret;
2010 for (const auto& part : parts) {
2011 const auto maybe = MaybeFind(nameMap, part);
2012 if (maybe) {
2013 ret << *maybe;
2014 } else {
2015 ret << part;
2018 const auto mappedName = ret.str();
2020 if (gl->WorkAroundDriverBugs() && gl->IsMesa()) {
2021 // Mesa incorrectly generates INVALID_OPERATION for gl_ prefixes here.
2022 if (mappedName.find("gl_") == 0) {
2023 return -1;
2027 return gl->fGetFragDataLocation(prog.mGLName, mappedName.c_str());
2030 // -
2032 WebGLContextBoundObject::WebGLContextBoundObject(WebGLContext* webgl)
2033 : mContext(webgl) {}
2035 } // namespace mozilla