no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / dom / canvas / WebGLContext.cpp
blobc1e6cbbed2b9fc8f13aee86d52dab386ea988c8d
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 "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 "LayerUserData.h"
29 #include "mozilla/dom/BindingUtils.h"
30 #include "mozilla/dom/ContentChild.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/ResultVariant.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/gfx/Swizzle.h"
58 #include "mozilla/layers/BufferTexture.h"
59 #include "mozilla/layers/RemoteTextureMap.h"
60 #include "mozilla/layers/CompositorBridgeChild.h"
61 #include "mozilla/layers/ImageBridgeChild.h"
62 #include "mozilla/layers/WebRenderUserData.h"
63 #include "mozilla/layers/WebRenderCanvasRenderer.h"
65 // Local
66 #include "CanvasUtils.h"
67 #include "ClientWebGLContext.h"
68 #include "HostWebGLContext.h"
69 #include "WebGLBuffer.h"
70 #include "WebGLChild.h"
71 #include "WebGLContextLossHandler.h"
72 #include "WebGLContextUtils.h"
73 #include "WebGLExtensions.h"
74 #include "WebGLFormats.h"
75 #include "WebGLFramebuffer.h"
76 #include "WebGLMemoryTracker.h"
77 #include "WebGLObjectModel.h"
78 #include "WebGLParent.h"
79 #include "WebGLProgram.h"
80 #include "WebGLQuery.h"
81 #include "WebGLSampler.h"
82 #include "WebGLShader.h"
83 #include "WebGLShaderValidator.h"
84 #include "WebGLSync.h"
85 #include "WebGLTransformFeedback.h"
86 #include "WebGLValidateStrings.h"
87 #include "WebGLVertexArray.h"
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 StaticMutex WebGLContext::sLruMutex;
105 std::list<WebGLContext*> WebGLContext::sLru;
107 WebGLContext::LruPosition::LruPosition() {
108 StaticMutexAutoLock lock(sLruMutex);
109 mItr = sLru.end();
110 } // NOLINT
112 WebGLContext::LruPosition::LruPosition(WebGLContext& context) {
113 StaticMutexAutoLock lock(sLruMutex);
114 mItr = sLru.insert(sLru.end(), &context);
117 void WebGLContext::LruPosition::AssignLocked(WebGLContext& aContext) {
118 ResetLocked();
119 mItr = sLru.insert(sLru.end(), &aContext);
122 void WebGLContext::LruPosition::ResetLocked() {
123 const auto end = sLru.end();
124 if (mItr != end) {
125 sLru.erase(mItr);
126 mItr = end;
130 void WebGLContext::LruPosition::Reset() {
131 StaticMutexAutoLock lock(sLruMutex);
132 ResetLocked();
135 bool WebGLContext::LruPosition::IsInsertedLocked() const {
136 return mItr != sLru.end();
139 WebGLContext::WebGLContext(HostWebGLContext* host,
140 const webgl::InitContextDesc& desc)
141 : gl(mGL_OnlyClearInDestroyResourcesAndContext), // const reference
142 mHost(host),
143 mResistFingerprinting(desc.resistFingerprinting),
144 mOptions(desc.options),
145 mPrincipalKey(desc.principalKey),
146 mContextLossHandler(this),
147 mRequestedSize(desc.size) {
148 if (host) {
149 host->mContext = this;
151 const FuncScope funcScope(*this, "<Create>");
152 WebGLMemoryTracker::EnsureRegistered();
155 WebGLContext::~WebGLContext() { DestroyResourcesAndContext(); }
157 void WebGLContext::DestroyResourcesAndContext() {
158 if (mRemoteTextureOwner) {
159 // Clean up any remote textures registered for framebuffer swap chains.
160 mRemoteTextureOwner->UnregisterAllTextureOwners();
161 mRemoteTextureOwner = nullptr;
164 if (!gl) return;
166 mDefaultFB = nullptr;
167 mResolvedDefaultFB = nullptr;
169 mBound2DTextures.Clear();
170 mBoundCubeMapTextures.Clear();
171 mBound3DTextures.Clear();
172 mBound2DArrayTextures.Clear();
173 mBoundSamplers.Clear();
174 mBoundArrayBuffer = nullptr;
175 mBoundCopyReadBuffer = nullptr;
176 mBoundCopyWriteBuffer = nullptr;
177 mBoundPixelPackBuffer = nullptr;
178 mBoundPixelUnpackBuffer = nullptr;
179 mBoundTransformFeedbackBuffer = nullptr;
180 mBoundUniformBuffer = nullptr;
181 mCurrentProgram = nullptr;
182 mActiveProgramLinkInfo = nullptr;
183 mBoundDrawFramebuffer = nullptr;
184 mBoundReadFramebuffer = nullptr;
185 mBoundVertexArray = nullptr;
186 mDefaultVertexArray = nullptr;
187 mBoundTransformFeedback = nullptr;
188 mDefaultTransformFeedback = nullptr;
190 mQuerySlot_SamplesPassed = nullptr;
191 mQuerySlot_TFPrimsWritten = nullptr;
192 mQuerySlot_TimeElapsed = nullptr;
194 mIndexedUniformBufferBindings.clear();
196 //////
198 if (mEmptyTFO) {
199 gl->fDeleteTransformFeedbacks(1, &mEmptyTFO);
200 mEmptyTFO = 0;
203 //////
205 if (mFakeVertexAttrib0BufferObject) {
206 gl->fDeleteBuffers(1, &mFakeVertexAttrib0BufferObject);
207 mFakeVertexAttrib0BufferObject = 0;
210 // disable all extensions except "WEBGL_lose_context". see bug #927969
211 // spec: http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
212 for (size_t i = 0; i < size_t(WebGLExtensionID::Max); ++i) {
213 WebGLExtensionID extension = WebGLExtensionID(i);
214 if (extension == WebGLExtensionID::WEBGL_lose_context) continue;
215 mExtensions[extension] = nullptr;
218 // We just got rid of everything, so the context had better
219 // have been going away.
220 if (gl::GLContext::ShouldSpew()) {
221 printf_stderr("--- WebGL context destroyed: %p\n", gl.get());
224 MOZ_ASSERT(gl);
225 gl->MarkDestroyed();
226 mGL_OnlyClearInDestroyResourcesAndContext = nullptr;
227 MOZ_ASSERT(!gl);
230 void ClientWebGLContext::MarkCanvasDirty() {
231 if (!mCanvasElement && !mOffscreenCanvas) return;
233 mFrameCaptureState = FrameCaptureState::DIRTY;
235 if (mIsCanvasDirty) return;
236 mIsCanvasDirty = true;
238 if (mCanvasElement) {
239 SVGObserverUtils::InvalidateDirectRenderingObservers(mCanvasElement);
240 mCanvasElement->InvalidateCanvasContent(nullptr);
241 } else if (mOffscreenCanvas) {
242 mOffscreenCanvas->QueueCommitToCompositor();
246 void WebGLContext::OnMemoryPressure() {
247 bool shouldLoseContext = mLoseContextOnMemoryPressure;
249 if (!mCanLoseContextInForeground &&
250 ProcessPriorityManager::CurrentProcessIsForeground()) {
251 shouldLoseContext = false;
254 if (shouldLoseContext) LoseContext();
257 // --
259 bool WebGLContext::CreateAndInitGL(
260 bool forceEnabled, std::vector<FailureReason>* const out_failReasons) {
261 const FuncScope funcScope(*this, "<Create>");
263 // WebGL2 is separately blocked:
264 if (IsWebGL2() && !forceEnabled) {
265 FailureReason reason;
266 if (!gfx::gfxVars::AllowWebgl2()) {
267 reason.info =
268 "AllowWebgl2:false restricts context creation on this system.";
269 out_failReasons->push_back(reason);
270 GenerateWarning("%s", reason.info.BeginReading());
271 return false;
275 auto flags = gl::CreateContextFlags::PREFER_ROBUSTNESS;
277 if (StaticPrefs::webgl_gl_khr_no_error()) {
278 flags |= gl::CreateContextFlags::NO_VALIDATION;
281 // -
283 if (StaticPrefs::webgl_forbid_hardware()) {
284 flags |= gl::CreateContextFlags::FORBID_HARDWARE;
286 if (StaticPrefs::webgl_forbid_software()) {
287 flags |= gl::CreateContextFlags::FORBID_SOFTWARE;
290 if (forceEnabled) {
291 flags &= ~gl::CreateContextFlags::FORBID_HARDWARE;
292 flags &= ~gl::CreateContextFlags::FORBID_SOFTWARE;
295 if ((flags & gl::CreateContextFlags::FORBID_HARDWARE) &&
296 (flags & gl::CreateContextFlags::FORBID_SOFTWARE)) {
297 FailureReason reason;
298 reason.info = "Both hardware and software were forbidden by config.";
299 out_failReasons->push_back(reason);
300 GenerateWarning("%s", reason.info.BeginReading());
301 return false;
304 // -
306 if (StaticPrefs::webgl_cgl_multithreaded()) {
307 flags |= gl::CreateContextFlags::PREFER_MULTITHREADED;
310 if (IsWebGL2()) {
311 flags |= gl::CreateContextFlags::PREFER_ES3;
312 } else {
313 // Request and prefer ES2 context for WebGL1.
314 flags |= gl::CreateContextFlags::PREFER_EXACT_VERSION;
316 if (!StaticPrefs::webgl_1_allow_core_profiles()) {
317 flags |= gl::CreateContextFlags::REQUIRE_COMPAT_PROFILE;
322 auto powerPref = mOptions.powerPreference;
324 // If "Use hardware acceleration when available" option is disabled:
325 if (!gfx::gfxConfig::IsEnabled(gfx::Feature::HW_COMPOSITING)) {
326 powerPref = dom::WebGLPowerPreference::Low_power;
329 const auto overrideVal = StaticPrefs::webgl_power_preference_override();
330 if (overrideVal > 0) {
331 powerPref = dom::WebGLPowerPreference::High_performance;
332 } else if (overrideVal < 0) {
333 powerPref = dom::WebGLPowerPreference::Low_power;
336 if (powerPref == dom::WebGLPowerPreference::High_performance) {
337 flags |= gl::CreateContextFlags::HIGH_POWER;
341 if (!gfx::gfxVars::WebglAllowCoreProfile()) {
342 flags |= gl::CreateContextFlags::REQUIRE_COMPAT_PROFILE;
345 // --
347 const bool useEGL = PR_GetEnv("MOZ_WEBGL_FORCE_EGL");
349 bool tryNativeGL = true;
350 bool tryANGLE = false;
352 #ifdef XP_WIN
353 tryNativeGL = false;
354 tryANGLE = true;
356 if (StaticPrefs::webgl_disable_wgl()) {
357 tryNativeGL = false;
360 if (StaticPrefs::webgl_disable_angle() ||
361 PR_GetEnv("MOZ_WEBGL_FORCE_OPENGL") || useEGL) {
362 tryNativeGL = true;
363 tryANGLE = false;
365 #endif
367 if (tryNativeGL && !forceEnabled) {
368 FailureReason reason;
369 if (!gfx::gfxVars::WebglAllowWindowsNativeGl()) {
370 reason.info =
371 "WebglAllowWindowsNativeGl:false restricts context creation on this "
372 "system.";
374 out_failReasons->push_back(reason);
376 GenerateWarning("%s", reason.info.BeginReading());
377 tryNativeGL = false;
381 // --
383 using fnCreateT = decltype(gl::GLContextProviderEGL::CreateHeadless);
384 const auto fnCreate = [&](fnCreateT* const pfnCreate,
385 const char* const info) {
386 nsCString failureId;
387 const RefPtr<gl::GLContext> gl = pfnCreate({flags}, &failureId);
388 if (!gl) {
389 out_failReasons->push_back(WebGLContext::FailureReason(failureId, info));
391 return gl;
394 const auto newGL = [&]() -> RefPtr<gl::GLContext> {
395 if (tryNativeGL) {
396 if (useEGL)
397 return fnCreate(&gl::GLContextProviderEGL::CreateHeadless, "useEGL");
399 const auto ret =
400 fnCreate(&gl::GLContextProvider::CreateHeadless, "tryNativeGL");
401 if (ret) return ret;
404 if (tryANGLE) {
405 return fnCreate(&gl::GLContextProviderEGL::CreateHeadless, "tryANGLE");
407 return nullptr;
408 }();
410 if (!newGL) {
411 out_failReasons->push_back(
412 FailureReason("FEATURE_FAILURE_WEBGL_EXHAUSTED_DRIVERS",
413 "Exhausted GL driver options."));
414 return false;
417 // --
419 FailureReason reason;
421 mGL_OnlyClearInDestroyResourcesAndContext = newGL;
422 MOZ_RELEASE_ASSERT(gl);
423 if (!InitAndValidateGL(&reason)) {
424 DestroyResourcesAndContext();
425 MOZ_RELEASE_ASSERT(!gl);
427 // The fail reason here should be specific enough for now.
428 out_failReasons->push_back(reason);
429 return false;
432 const auto val = StaticPrefs::webgl_debug_incomplete_tex_color();
433 if (val) {
434 mIncompleteTexOverride.reset(new gl::Texture(*gl));
435 const gl::ScopedBindTexture autoBind(gl, mIncompleteTexOverride->name);
436 const auto heapVal = std::make_unique<uint32_t>(val);
437 gl->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGBA, 1, 1, 0,
438 LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, heapVal.get());
441 return true;
444 // Fallback for resizes:
446 bool WebGLContext::EnsureDefaultFB() {
447 if (mDefaultFB) {
448 MOZ_ASSERT(*uvec2::FromSize(mDefaultFB->mSize) == mRequestedSize);
449 return true;
452 const bool depthStencil = mOptions.depth || mOptions.stencil;
453 auto attemptSize = gfx::IntSize{mRequestedSize.x, mRequestedSize.y};
455 while (attemptSize.width || attemptSize.height) {
456 attemptSize.width = std::max(attemptSize.width, 1);
457 attemptSize.height = std::max(attemptSize.height, 1);
459 [&]() {
460 if (mOptions.antialias) {
461 MOZ_ASSERT(!mDefaultFB);
462 mDefaultFB = gl::MozFramebuffer::Create(gl, attemptSize, mMsaaSamples,
463 depthStencil);
464 if (mDefaultFB) return;
465 if (mOptionsFrozen) return;
468 MOZ_ASSERT(!mDefaultFB);
469 mDefaultFB = gl::MozFramebuffer::Create(gl, attemptSize, 0, depthStencil);
470 }();
472 if (mDefaultFB) break;
474 attemptSize.width /= 2;
475 attemptSize.height /= 2;
478 if (!mDefaultFB) {
479 GenerateWarning("Backbuffer resize failed. Losing context.");
480 LoseContext();
481 return false;
484 mDefaultFB_IsInvalid = true;
486 const auto actualSize = *uvec2::FromSize(mDefaultFB->mSize);
487 if (actualSize != mRequestedSize) {
488 GenerateWarning(
489 "Requested size %ux%u was too large, but resize"
490 " to %ux%u succeeded.",
491 mRequestedSize.x, mRequestedSize.y, actualSize.x, actualSize.y);
493 mRequestedSize = actualSize;
494 return true;
497 void WebGLContext::Resize(uvec2 requestedSize) {
498 // Zero-sized surfaces can cause problems.
499 if (!requestedSize.x) {
500 requestedSize.x = 1;
502 if (!requestedSize.y) {
503 requestedSize.y = 1;
506 // Kill our current default fb(s), for later lazy allocation.
507 mRequestedSize = requestedSize;
508 mDefaultFB = nullptr;
509 mResetLayer = true; // New size means new Layer.
512 UniquePtr<webgl::FormatUsageAuthority> WebGLContext::CreateFormatUsage(
513 gl::GLContext* gl) const {
514 return webgl::FormatUsageAuthority::CreateForWebGL1(gl);
517 /*static*/
518 RefPtr<WebGLContext> WebGLContext::Create(HostWebGLContext* host,
519 const webgl::InitContextDesc& desc,
520 webgl::InitContextResult* const out) {
521 AUTO_PROFILER_LABEL("WebGLContext::Create", GRAPHICS);
522 nsCString failureId = "FEATURE_FAILURE_WEBGL_UNKOWN"_ns;
523 const bool forceEnabled = StaticPrefs::webgl_force_enabled();
524 ScopedGfxFeatureReporter reporter("WebGL", forceEnabled);
526 auto res = [&]() -> Result<RefPtr<WebGLContext>, std::string> {
527 bool disabled = StaticPrefs::webgl_disabled();
529 // TODO: When we have software webgl support we should use that instead.
530 disabled |= gfxPlatform::InSafeMode();
532 if (disabled) {
533 if (gfxPlatform::InSafeMode()) {
534 failureId = "FEATURE_FAILURE_WEBGL_SAFEMODE"_ns;
535 } else {
536 failureId = "FEATURE_FAILURE_WEBGL_DISABLED"_ns;
538 return Err("WebGL is currently disabled.");
541 // Alright, now let's start trying.
543 RefPtr<WebGLContext> webgl;
544 if (desc.isWebgl2) {
545 webgl = new WebGL2Context(host, desc);
546 } else {
547 webgl = new WebGLContext(host, desc);
550 MOZ_ASSERT(!webgl->gl);
551 std::vector<FailureReason> failReasons;
552 if (!webgl->CreateAndInitGL(forceEnabled, &failReasons)) {
553 nsCString text("WebGL creation failed: ");
554 for (const auto& cur : failReasons) {
555 // Don't try to accumulate using an empty key if |cur.key| is empty.
556 if (cur.key.IsEmpty()) {
557 Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_FAILURE_ID,
558 "FEATURE_FAILURE_REASON_UNKNOWN"_ns);
559 } else {
560 Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_FAILURE_ID, cur.key);
563 const auto str = nsPrintfCString("\n* %s (%s)", cur.info.BeginReading(),
564 cur.key.BeginReading());
565 text.Append(str);
567 failureId = "FEATURE_FAILURE_REASON"_ns;
568 return Err(text.BeginReading());
570 MOZ_ASSERT(webgl->gl);
572 if (desc.options.failIfMajorPerformanceCaveat) {
573 if (webgl->gl->IsWARP()) {
574 failureId = "FEATURE_FAILURE_WEBGL_PERF_WARP"_ns;
575 return Err(
576 "failIfMajorPerformanceCaveat: Driver is not"
577 " hardware-accelerated.");
580 #ifdef XP_WIN
581 if (webgl->gl->GetContextType() == gl::GLContextType::WGL &&
582 !gl::sWGLLib.HasDXInterop2()) {
583 failureId = "FEATURE_FAILURE_WEBGL_DXGL_INTEROP2"_ns;
584 return Err("failIfMajorPerformanceCaveat: WGL without DXGLInterop2.");
586 #endif
589 const FuncScope funcScope(*webgl, "getContext/restoreContext");
591 MOZ_ASSERT(!webgl->mDefaultFB);
592 if (!webgl->EnsureDefaultFB()) {
593 MOZ_ASSERT(!webgl->mDefaultFB);
594 MOZ_ASSERT(webgl->IsContextLost());
595 failureId = "FEATURE_FAILURE_WEBGL_BACKBUFFER"_ns;
596 return Err("Initializing WebGL backbuffer failed.");
599 return webgl;
600 }();
601 if (res.isOk()) {
602 failureId = "SUCCESS"_ns;
604 Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_FAILURE_ID, failureId);
606 if (!res.isOk()) {
607 out->error = res.unwrapErr();
608 return nullptr;
610 const auto webgl = res.unwrap();
612 // Update our internal stuff:
613 webgl->FinishInit();
615 reporter.SetSuccessful();
616 if (gl::GLContext::ShouldSpew()) {
617 printf_stderr("--- WebGL context created: %p\n", webgl.get());
620 // -
622 const auto UploadableSdTypes = [&]() {
623 webgl::EnumMask<layers::SurfaceDescriptor::Type> types;
624 types[layers::SurfaceDescriptor::TSurfaceDescriptorBuffer] = true;
625 // This is conditional on not using the Compositor thread because we may
626 // need to synchronize with the RDD process over the PVideoBridge protocol
627 // to wait for the texture to be available in the compositor process. We
628 // cannot block on the Compositor thread, so in that configuration, we would
629 // prefer to do the readback from the RDD which is guaranteed to work, and
630 // only block the owning thread for WebGL.
631 types[layers::SurfaceDescriptor::TSurfaceDescriptorGPUVideo] =
632 gfx::gfxVars::UseCanvasRenderThread() ||
633 !gfx::gfxVars::SupportsThreadsafeGL();
634 if (webgl->gl->IsANGLE()) {
635 types[layers::SurfaceDescriptor::TSurfaceDescriptorD3D10] = true;
636 types[layers::SurfaceDescriptor::TSurfaceDescriptorDXGIYCbCr] = true;
638 if (kIsMacOS) {
639 types[layers::SurfaceDescriptor::TSurfaceDescriptorMacIOSurface] = true;
641 if (kIsAndroid) {
642 types[layers::SurfaceDescriptor::TSurfaceTextureDescriptor] = true;
644 if (kIsLinux) {
645 types[layers::SurfaceDescriptor::TSurfaceDescriptorDMABuf] = true;
647 return types;
650 // -
652 out->options = webgl->mOptions;
653 out->limits = *webgl->mLimits;
654 out->uploadableSdTypes = UploadableSdTypes();
655 out->vendor = webgl->gl->Vendor();
656 out->isRgb8Renderable = webgl->mIsRgb8Renderable;
658 return webgl;
661 void WebGLContext::FinishInit() {
662 mOptions.antialias &= bool(mDefaultFB->mSamples);
664 if (!mOptions.alpha) {
665 // We always have alpha.
666 mNeedsFakeNoAlpha = true;
669 if (mOptions.depth || mOptions.stencil) {
670 // We always have depth+stencil if we have either.
671 if (!mOptions.depth) {
672 mNeedsFakeNoDepth = true;
674 if (!mOptions.stencil) {
675 mNeedsFakeNoStencil = true;
679 mResetLayer = true;
680 mOptionsFrozen = true;
682 //////
683 // Initial setup.
685 gl->mImplicitMakeCurrent = true;
686 gl->mElideDuplicateBindFramebuffers = true;
688 const auto& size = mDefaultFB->mSize;
690 mViewportX = mViewportY = 0;
691 mViewportWidth = size.width;
692 mViewportHeight = size.height;
693 gl->fViewport(mViewportX, mViewportY, mViewportWidth, mViewportHeight);
695 mScissorRect = {0, 0, size.width, size.height};
696 mScissorRect.Apply(*gl);
698 //////
699 // Check everything
701 AssertCachedBindings();
702 AssertCachedGlobalState();
704 mShouldPresent = true;
706 //////
707 // mIsRgb8Renderable
710 const auto tex = gl::ScopedTexture(gl);
711 const auto fb = gl::ScopedFramebuffer(gl);
712 gl->fBindTexture(LOCAL_GL_TEXTURE_2D, tex);
713 gl->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGB, 1, 1, 0, LOCAL_GL_RGB,
714 LOCAL_GL_UNSIGNED_BYTE, nullptr);
716 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fb);
717 gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
718 LOCAL_GL_TEXTURE_2D, tex, 0);
720 const auto status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
721 mIsRgb8Renderable = (status == LOCAL_GL_FRAMEBUFFER_COMPLETE);
724 //////
726 gl->ResetSyncCallCount("WebGLContext Initialization");
727 LoseLruContextIfLimitExceeded();
730 void WebGLContext::SetCompositableHost(
731 RefPtr<layers::CompositableHost>& aCompositableHost) {
732 mCompositableHost = aCompositableHost;
735 void WebGLContext::BumpLruLocked() {
736 if (!mIsContextLost && !mPendingContextLoss) {
737 mLruPosition.AssignLocked(*this);
738 } else {
739 MOZ_ASSERT(!mLruPosition.IsInsertedLocked());
743 void WebGLContext::BumpLru() {
744 StaticMutexAutoLock lock(sLruMutex);
745 BumpLruLocked();
748 void WebGLContext::LoseLruContextIfLimitExceeded() {
749 StaticMutexAutoLock lock(sLruMutex);
751 const auto maxContexts = std::max(1u, StaticPrefs::webgl_max_contexts());
752 const auto maxContextsPerPrincipal =
753 std::max(1u, StaticPrefs::webgl_max_contexts_per_principal());
755 // it's important to update the index on a new context before losing old
756 // contexts, otherwise new unused contexts would all have index 0 and we
757 // couldn't distinguish older ones when choosing which one to lose first.
758 BumpLruLocked();
761 size_t forPrincipal = 0;
762 for (const auto& context : sLru) {
763 if (context->mPrincipalKey == mPrincipalKey) {
764 forPrincipal += 1;
768 while (forPrincipal > maxContextsPerPrincipal) {
769 const auto text = nsPrintfCString(
770 "Exceeded %u live WebGL contexts for this principal, losing the "
771 "least recently used one.",
772 maxContextsPerPrincipal);
773 JsWarning(ToString(text));
775 for (const auto& context : sLru) {
776 if (context->mPrincipalKey == mPrincipalKey) {
777 MOZ_ASSERT(context != this);
778 context->LoseContextLruLocked(webgl::ContextLossReason::None);
779 forPrincipal -= 1;
780 break;
786 auto total = sLru.size();
787 while (total > maxContexts) {
788 const auto text = nsPrintfCString(
789 "Exceeded %u live WebGL contexts, losing the least "
790 "recently used one.",
791 maxContexts);
792 JsWarning(ToString(text));
794 const auto& context = sLru.front();
795 MOZ_ASSERT(context != this);
796 context->LoseContextLruLocked(webgl::ContextLossReason::None);
797 total -= 1;
801 // -
803 namespace webgl {
805 ScopedPrepForResourceClear::ScopedPrepForResourceClear(
806 const WebGLContext& webgl_)
807 : webgl(webgl_) {
808 const auto& gl = webgl.gl;
810 if (webgl.mScissorTestEnabled) {
811 gl->fDisable(LOCAL_GL_SCISSOR_TEST);
813 if (webgl.mRasterizerDiscardEnabled) {
814 gl->fDisable(LOCAL_GL_RASTERIZER_DISCARD);
817 // "The clear operation always uses the front stencil write mask
818 // when clearing the stencil buffer."
819 webgl.DoColorMask(Some(0), 0b1111);
820 gl->fDepthMask(true);
821 gl->fStencilMaskSeparate(LOCAL_GL_FRONT, 0xffffffff);
823 gl->fClearColor(0.0f, 0.0f, 0.0f, 0.0f);
824 gl->fClearDepth(1.0f); // Depth formats are always cleared to 1.0f, not 0.0f.
825 gl->fClearStencil(0);
828 ScopedPrepForResourceClear::~ScopedPrepForResourceClear() {
829 const auto& gl = webgl.gl;
831 if (webgl.mScissorTestEnabled) {
832 gl->fEnable(LOCAL_GL_SCISSOR_TEST);
834 if (webgl.mRasterizerDiscardEnabled) {
835 gl->fEnable(LOCAL_GL_RASTERIZER_DISCARD);
838 webgl.DoColorMask(Some(0), webgl.mColorWriteMask0);
839 gl->fDepthMask(webgl.mDepthWriteMask);
840 gl->fStencilMaskSeparate(LOCAL_GL_FRONT, webgl.mStencilWriteMaskFront);
842 gl->fClearColor(webgl.mColorClearValue[0], webgl.mColorClearValue[1],
843 webgl.mColorClearValue[2], webgl.mColorClearValue[3]);
844 gl->fClearDepth(webgl.mDepthClearValue);
845 gl->fClearStencil(webgl.mStencilClearValue);
848 } // namespace webgl
850 // -
852 void WebGLContext::OnEndOfFrame() {
853 if (StaticPrefs::webgl_perf_spew_frame_allocs()) {
854 GeneratePerfWarning("[webgl.perf.spew-frame-allocs] %" PRIu64
855 " data allocations this frame.",
856 mDataAllocGLCallCount);
858 mDataAllocGLCallCount = 0;
859 gl->ResetSyncCallCount("WebGLContext PresentScreenBuffer");
861 mDrawCallsSinceLastFlush = 0;
863 BumpLru();
866 void WebGLContext::BlitBackbufferToCurDriverFB(
867 WebGLFramebuffer* const srcAsWebglFb,
868 const gl::MozFramebuffer* const srcAsMozFb, bool srcIsBGRA) const {
869 // BlitFramebuffer ignores ColorMask().
871 if (mScissorTestEnabled) {
872 gl->fDisable(LOCAL_GL_SCISSOR_TEST);
875 [&]() {
876 // If a MozFramebuffer is supplied, ensure that a WebGLFramebuffer is not
877 // used since it might not have completeness info, while the MozFramebuffer
878 // can still supply the needed information.
879 MOZ_ASSERT(!(srcAsMozFb && srcAsWebglFb));
880 const auto* mozFb = srcAsMozFb ? srcAsMozFb : mDefaultFB.get();
881 GLuint fbo = 0;
882 gfx::IntSize size;
883 if (srcAsWebglFb) {
884 fbo = srcAsWebglFb->mGLName;
885 const auto* info = srcAsWebglFb->GetCompletenessInfo();
886 MOZ_ASSERT(info);
887 size = gfx::IntSize(info->width, info->height);
888 } else {
889 fbo = mozFb->mFB;
890 size = mozFb->mSize;
893 // If no format conversion is necessary, then attempt to directly blit
894 // between framebuffers. Otherwise, if we need to convert to RGBA from
895 // the source format, then we will need to use the texture blit path
896 // below.
897 if (!srcIsBGRA) {
898 if (gl->IsSupported(gl::GLFeature::framebuffer_blit)) {
899 gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, fbo);
900 gl->fBlitFramebuffer(0, 0, size.width, size.height, 0, 0, size.width,
901 size.height, LOCAL_GL_COLOR_BUFFER_BIT,
902 LOCAL_GL_NEAREST);
903 return;
905 if (mDefaultFB->mSamples &&
906 gl->IsExtensionSupported(
907 gl::GLContext::APPLE_framebuffer_multisample)) {
908 gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, fbo);
909 gl->fResolveMultisampleFramebufferAPPLE();
910 return;
914 GLuint colorTex = 0;
915 if (srcAsWebglFb) {
916 const auto& attach = srcAsWebglFb->ColorAttachment0();
917 MOZ_ASSERT(attach.Texture());
918 colorTex = attach.Texture()->mGLName;
919 } else {
920 colorTex = mozFb->ColorTex();
923 // DrawBlit handles ColorMask itself.
924 gl->BlitHelper()->DrawBlitTextureToFramebuffer(
925 colorTex, size, size, LOCAL_GL_TEXTURE_2D, srcIsBGRA);
926 }();
928 if (mScissorTestEnabled) {
929 gl->fEnable(LOCAL_GL_SCISSOR_TEST);
933 // -
935 template <typename T, typename... Args>
936 constexpr auto MakeArray(Args... args) -> std::array<T, sizeof...(Args)> {
937 return {{static_cast<T>(args)...}};
940 inline gfx::ColorSpace2 ToColorSpace2(const WebGLContextOptions& options) {
941 auto ret = gfx::ColorSpace2::UNKNOWN;
942 if (true) {
943 ret = gfx::ColorSpace2::SRGB;
945 if (!options.ignoreColorSpace) {
946 ret = gfx::ToColorSpace2(options.colorSpace);
948 return ret;
951 // -
953 // For an overview of how WebGL compositing works, see:
954 // https://wiki.mozilla.org/Platform/GFX/WebGL/Compositing
955 bool WebGLContext::PresentInto(gl::SwapChain& swapChain) {
956 OnEndOfFrame();
958 if (!ValidateAndInitFB(nullptr)) return false;
961 const auto colorSpace = ToColorSpace2(mOptions);
962 auto presenter = swapChain.Acquire(mDefaultFB->mSize, colorSpace);
963 if (!presenter) {
964 GenerateWarning("Swap chain surface creation failed.");
965 LoseContext();
966 return false;
969 const auto destFb = presenter->Fb();
970 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, destFb);
972 BlitBackbufferToCurDriverFB();
974 if (!mOptions.preserveDrawingBuffer) {
975 if (gl->IsSupported(gl::GLFeature::invalidate_framebuffer)) {
976 gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, mDefaultFB->mFB);
977 constexpr auto attachments = MakeArray<GLenum>(
978 LOCAL_GL_COLOR_ATTACHMENT0, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT);
979 gl->fInvalidateFramebuffer(LOCAL_GL_READ_FRAMEBUFFER,
980 attachments.size(), attachments.data());
982 mDefaultFB_IsInvalid = true;
985 #ifdef DEBUG
986 if (!mOptions.alpha) {
987 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, destFb);
988 gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, 4);
989 if (IsWebGL2()) {
990 gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, 0);
991 gl->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, 0);
992 gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, 0);
994 uint32_t pixel = 0xffbadbad;
995 gl->fReadPixels(0, 0, 1, 1, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE,
996 &pixel);
997 MOZ_ASSERT((pixel & 0xff000000) == 0xff000000);
999 #endif
1002 return true;
1005 bool WebGLContext::PresentIntoXR(gl::SwapChain& swapChain,
1006 const gl::MozFramebuffer& fb) {
1007 OnEndOfFrame();
1009 const auto colorSpace = ToColorSpace2(mOptions);
1010 auto presenter = swapChain.Acquire(fb.mSize, colorSpace);
1011 if (!presenter) {
1012 GenerateWarning("Swap chain surface creation failed.");
1013 LoseContext();
1014 return false;
1017 const auto destFb = presenter->Fb();
1018 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, destFb);
1020 BlitBackbufferToCurDriverFB(nullptr, &fb);
1022 // https://immersive-web.github.io/webxr/#opaque-framebuffer
1023 // Opaque framebuffers will always be cleared regardless of the
1024 // associated WebGL context’s preserveDrawingBuffer value.
1025 if (gl->IsSupported(gl::GLFeature::invalidate_framebuffer)) {
1026 gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, fb.mFB);
1027 constexpr auto attachments = MakeArray<GLenum>(
1028 LOCAL_GL_COLOR_ATTACHMENT0, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT);
1029 gl->fInvalidateFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, attachments.size(),
1030 attachments.data());
1033 return true;
1036 // Initialize a swap chain's surface factory given the desired surface type.
1037 void InitSwapChain(gl::GLContext& gl, gl::SwapChain& swapChain,
1038 const layers::TextureType consumerType) {
1039 if (!swapChain.mFactory) {
1040 auto typedFactory = gl::SurfaceFactory::Create(&gl, consumerType);
1041 if (typedFactory) {
1042 swapChain.mFactory = std::move(typedFactory);
1045 if (!swapChain.mFactory) {
1046 NS_WARNING("Failed to make an ideal SurfaceFactory.");
1047 swapChain.mFactory = MakeUnique<gl::SurfaceFactory_Basic>(gl);
1049 MOZ_ASSERT(swapChain.mFactory);
1052 void WebGLContext::Present(WebGLFramebuffer* const xrFb,
1053 const layers::TextureType consumerType,
1054 const bool webvr,
1055 const webgl::SwapChainOptions& options) {
1056 const FuncScope funcScope(*this, "<Present>");
1057 if (IsContextLost()) {
1058 EnsureContextLostRemoteTextureOwner(options);
1059 return;
1062 auto swapChain = GetSwapChain(xrFb, webvr);
1063 const gl::MozFramebuffer* maybeFB = nullptr;
1064 if (xrFb) {
1065 maybeFB = xrFb->mOpaque.get();
1066 } else {
1067 mResolvedDefaultFB = nullptr;
1070 InitSwapChain(*gl, *swapChain, consumerType);
1072 bool valid =
1073 maybeFB ? PresentIntoXR(*swapChain, *maybeFB) : PresentInto(*swapChain);
1074 if (!valid) {
1075 EnsureContextLostRemoteTextureOwner(options);
1076 return;
1079 bool useAsync = options.remoteTextureOwnerId.IsValid() &&
1080 options.remoteTextureId.IsValid();
1081 if (useAsync) {
1082 PushRemoteTexture(nullptr, *swapChain, swapChain->FrontBuffer(), options);
1086 void WebGLContext::WaitForTxn(layers::RemoteTextureOwnerId ownerId,
1087 layers::RemoteTextureTxnType txnType,
1088 layers::RemoteTextureTxnId txnId) {
1089 if (!ownerId.IsValid() || !txnType || !txnId) {
1090 return;
1092 if (mRemoteTextureOwner && mRemoteTextureOwner->IsRegistered(ownerId)) {
1093 mRemoteTextureOwner->WaitForTxn(ownerId, txnType, txnId);
1097 bool WebGLContext::CopyToSwapChain(
1098 WebGLFramebuffer* const srcFb, const layers::TextureType consumerType,
1099 const webgl::SwapChainOptions& options,
1100 layers::RemoteTextureOwnerClient* ownerClient) {
1101 const FuncScope funcScope(*this, "<CopyToSwapChain>");
1102 if (IsContextLost()) {
1103 return false;
1106 OnEndOfFrame();
1108 if (!srcFb) {
1109 return false;
1111 const auto* info = srcFb->GetCompletenessInfo();
1112 if (!info) {
1113 return false;
1115 gfx::IntSize size(info->width, info->height);
1117 InitSwapChain(*gl, srcFb->mSwapChain, consumerType);
1119 bool useAsync = options.remoteTextureOwnerId.IsValid() &&
1120 options.remoteTextureId.IsValid();
1121 // If we're using async present and if there is no way to serialize surfaces,
1122 // then a readback is required to do the copy. In this case, there's no reason
1123 // to copy into a separate shared surface for the front buffer. Just directly
1124 // read back the WebGL framebuffer into and push it as a remote texture.
1125 if (useAsync && srcFb->mSwapChain.mFactory->GetConsumerType() ==
1126 layers::TextureType::Unknown) {
1127 return PushRemoteTexture(srcFb, srcFb->mSwapChain, nullptr, options,
1128 ownerClient);
1132 // ColorSpace will need to be part of SwapChainOptions for DTWebgl.
1133 const auto colorSpace = ToColorSpace2(mOptions);
1134 auto presenter = srcFb->mSwapChain.Acquire(size, colorSpace);
1135 if (!presenter) {
1136 GenerateWarning("Swap chain surface creation failed.");
1137 LoseContext();
1138 return false;
1141 const ScopedFBRebinder saveFB(this);
1143 const auto destFb = presenter->Fb();
1144 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, destFb);
1146 BlitBackbufferToCurDriverFB(srcFb, nullptr, options.bgra);
1149 if (useAsync) {
1150 return PushRemoteTexture(srcFb, srcFb->mSwapChain,
1151 srcFb->mSwapChain.FrontBuffer(), options,
1152 ownerClient);
1154 return true;
1157 bool WebGLContext::PushRemoteTexture(
1158 WebGLFramebuffer* fb, gl::SwapChain& swapChain,
1159 std::shared_ptr<gl::SharedSurface> surf,
1160 const webgl::SwapChainOptions& options,
1161 layers::RemoteTextureOwnerClient* ownerClient) {
1162 const auto onFailure = [&]() -> bool {
1163 GenerateWarning("Remote texture creation failed.");
1164 LoseContext();
1165 if (ownerClient && ownerClient == mRemoteTextureOwner) {
1166 ownerClient->PushDummyTexture(options.remoteTextureId,
1167 options.remoteTextureOwnerId);
1169 return false;
1172 if (!ownerClient) {
1173 if (!mRemoteTextureOwner) {
1174 // Ensure we have a remote texture owner client for WebGLParent.
1175 const auto* outOfProcess =
1176 mHost ? mHost->mOwnerData.outOfProcess : nullptr;
1177 if (!outOfProcess) {
1178 return onFailure();
1180 auto pid = outOfProcess->OtherPid();
1181 mRemoteTextureOwner = MakeRefPtr<layers::RemoteTextureOwnerClient>(pid);
1183 ownerClient = mRemoteTextureOwner;
1186 layers::RemoteTextureOwnerId ownerId = options.remoteTextureOwnerId;
1187 layers::RemoteTextureId textureId = options.remoteTextureId;
1189 if (!ownerClient->IsRegistered(ownerId)) {
1190 // Register a texture owner to represent the swap chain.
1191 RefPtr<layers::RemoteTextureOwnerClient> textureOwner = ownerClient;
1192 auto destroyedCallback = [textureOwner, ownerId]() {
1193 textureOwner->UnregisterTextureOwner(ownerId);
1196 swapChain.SetDestroyedCallback(destroyedCallback);
1197 ownerClient->RegisterTextureOwner(ownerId,
1198 /* aSharedRecycling */ !!fb);
1201 MOZ_ASSERT(fb || surf);
1202 gfx::IntSize size;
1203 if (surf) {
1204 size = surf->mDesc.size;
1205 } else {
1206 const auto* info = fb->GetCompletenessInfo();
1207 MOZ_ASSERT(info);
1208 size = gfx::IntSize(info->width, info->height);
1211 const auto surfaceFormat = mOptions.alpha ? gfx::SurfaceFormat::B8G8R8A8
1212 : gfx::SurfaceFormat::B8G8R8X8;
1213 Maybe<layers::SurfaceDescriptor> desc;
1214 if (surf) {
1215 desc = surf->ToSurfaceDescriptor();
1217 if (!desc) {
1218 if (surf && surf->mDesc.type != gl::SharedSurfaceType::Basic) {
1219 return onFailure();
1221 // If we can't serialize to a surface descriptor, then we need to create
1222 // a buffer to read back into that will become the remote texture.
1223 auto data = ownerClient->CreateOrRecycleBufferTextureData(
1224 size, surfaceFormat, ownerId);
1225 if (!data) {
1226 gfxCriticalNoteOnce << "Failed to allocate BufferTextureData";
1227 return onFailure();
1230 layers::MappedTextureData mappedData;
1231 if (!data->BorrowMappedData(mappedData)) {
1232 return onFailure();
1235 Range<uint8_t> range = {mappedData.data,
1236 data->AsBufferTextureData()->GetBufferSize()};
1238 // If we have a surface representing the front buffer, then try to snapshot
1239 // that. Otherwise, when there is no surface, we read back directly from the
1240 // WebGL framebuffer.
1241 auto valid =
1242 surf ? FrontBufferSnapshotInto(surf, Some(range),
1243 Some(mappedData.stride))
1244 : SnapshotInto(fb->mGLName, size, range, Some(mappedData.stride));
1245 if (!valid) {
1246 return onFailure();
1249 if (!options.bgra) {
1250 // If the buffer is already BGRA, we don't need to swizzle. However, if it
1251 // is RGBA, then a swizzle to BGRA is required.
1252 bool rv = gfx::SwizzleData(mappedData.data, mappedData.stride,
1253 gfx::SurfaceFormat::R8G8B8A8, mappedData.data,
1254 mappedData.stride,
1255 gfx::SurfaceFormat::B8G8R8A8, mappedData.size);
1256 MOZ_RELEASE_ASSERT(rv, "SwizzleData failed!");
1259 ownerClient->PushTexture(textureId, ownerId, std::move(data));
1260 return true;
1263 // SharedSurfaces of SurfaceDescriptorD3D10 and SurfaceDescriptorMacIOSurface
1264 // need to be kept alive. They will be recycled by
1265 // RemoteTextureOwnerClient::GetRecycledSharedSurface() when their usages are
1266 // ended.
1267 std::shared_ptr<gl::SharedSurface> keepAlive;
1268 switch (desc->type()) {
1269 case layers::SurfaceDescriptor::TSurfaceDescriptorD3D10:
1270 case layers::SurfaceDescriptor::TSurfaceDescriptorMacIOSurface:
1271 case layers::SurfaceDescriptor::TSurfaceTextureDescriptor:
1272 case layers::SurfaceDescriptor::TSurfaceDescriptorAndroidHardwareBuffer:
1273 case layers::SurfaceDescriptor::TSurfaceDescriptorDMABuf:
1274 keepAlive = surf;
1275 break;
1276 default:
1277 break;
1280 ownerClient->PushTexture(textureId, ownerId, keepAlive, size, surfaceFormat,
1281 *desc);
1283 // Look for a recycled surface that matches the swap chain.
1284 while (auto recycledSurface = ownerClient->GetRecycledSharedSurface(
1285 size, surfaceFormat, desc->type(), ownerId)) {
1286 if (swapChain.StoreRecycledSurface(recycledSurface)) {
1287 break;
1290 return true;
1293 void WebGLContext::EnsureContextLostRemoteTextureOwner(
1294 const webgl::SwapChainOptions& options) {
1295 if (!options.remoteTextureOwnerId.IsValid()) {
1296 return;
1299 if (!mRemoteTextureOwner) {
1300 // Ensure we have a remote texture owner client for WebGLParent.
1301 const auto* outOfProcess = mHost ? mHost->mOwnerData.outOfProcess : nullptr;
1302 if (!outOfProcess) {
1303 return;
1305 auto pid = outOfProcess->OtherPid();
1306 mRemoteTextureOwner = MakeRefPtr<layers::RemoteTextureOwnerClient>(pid);
1309 layers::RemoteTextureOwnerId ownerId = options.remoteTextureOwnerId;
1311 if (!mRemoteTextureOwner->IsRegistered(ownerId)) {
1312 mRemoteTextureOwner->RegisterTextureOwner(ownerId);
1314 mRemoteTextureOwner->NotifyContextLost();
1317 void WebGLContext::EndOfFrame() {
1318 const FuncScope funcScope(*this, "<EndOfFrame>");
1319 if (IsContextLost()) return;
1321 OnEndOfFrame();
1324 gl::SwapChain* WebGLContext::GetSwapChain(WebGLFramebuffer* const xrFb,
1325 const bool webvr) {
1326 auto swapChain = webvr ? &mWebVRSwapChain : &mSwapChain;
1327 if (xrFb) {
1328 swapChain = &xrFb->mSwapChain;
1330 return swapChain;
1333 Maybe<layers::SurfaceDescriptor> WebGLContext::GetFrontBuffer(
1334 WebGLFramebuffer* const xrFb, const bool webvr) {
1335 auto* swapChain = GetSwapChain(xrFb, webvr);
1336 if (!swapChain) return {};
1337 const auto& front = swapChain->FrontBuffer();
1338 if (!front) return {};
1340 return front->ToSurfaceDescriptor();
1343 Maybe<uvec2> WebGLContext::FrontBufferSnapshotInto(
1344 const Maybe<Range<uint8_t>> maybeDest, const Maybe<size_t> destStride) {
1345 const auto& front = mSwapChain.FrontBuffer();
1346 if (!front) return {};
1347 return FrontBufferSnapshotInto(front, maybeDest, destStride);
1350 Maybe<uvec2> WebGLContext::FrontBufferSnapshotInto(
1351 const std::shared_ptr<gl::SharedSurface>& front,
1352 const Maybe<Range<uint8_t>> maybeDest, const Maybe<size_t> destStride) {
1353 const auto& size = front->mDesc.size;
1354 if (!maybeDest) return Some(*uvec2::FromSize(size));
1356 // -
1358 front->WaitForBufferOwnership();
1359 front->LockProd();
1360 front->ProducerReadAcquire();
1361 auto reset = MakeScopeExit([&] {
1362 front->ProducerReadRelease();
1363 front->UnlockProd();
1366 // -
1368 return SnapshotInto(front->mFb ? front->mFb->mFB : 0, size, *maybeDest,
1369 destStride);
1372 Maybe<uvec2> WebGLContext::SnapshotInto(GLuint srcFb, const gfx::IntSize& size,
1373 const Range<uint8_t>& dest,
1374 const Maybe<size_t> destStride) {
1375 const auto minStride = CheckedInt<size_t>(size.width) * 4;
1376 if (!minStride.isValid()) {
1377 gfxCriticalError() << "SnapshotInto: invalid stride, width:" << size.width;
1378 return {};
1380 size_t stride = destStride.valueOr(minStride.value());
1381 if (stride < minStride.value() || (stride % 4) != 0) {
1382 gfxCriticalError() << "SnapshotInto: invalid stride, width:" << size.width
1383 << ", stride:" << stride;
1384 return {};
1387 gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, 1);
1388 if (IsWebGL2()) {
1389 gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH,
1390 stride > minStride.value() ? stride / 4 : 0);
1391 gl->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, 0);
1392 gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, 0);
1395 // -
1397 const auto readFbWas = mBoundReadFramebuffer;
1398 const auto pboWas = mBoundPixelPackBuffer;
1400 GLenum fbTarget = LOCAL_GL_READ_FRAMEBUFFER;
1401 if (!IsWebGL2()) {
1402 fbTarget = LOCAL_GL_FRAMEBUFFER;
1404 auto reset2 = MakeScopeExit([&] {
1405 DoBindFB(readFbWas, fbTarget);
1406 if (pboWas) {
1407 BindBuffer(LOCAL_GL_PIXEL_PACK_BUFFER, pboWas);
1411 gl->fBindFramebuffer(fbTarget, srcFb);
1412 if (pboWas) {
1413 BindBuffer(LOCAL_GL_PIXEL_PACK_BUFFER, nullptr);
1416 // -
1418 const auto srcByteCount = CheckedInt<size_t>(stride) * size.height;
1419 if (!srcByteCount.isValid()) {
1420 gfxCriticalError() << "SnapshotInto: invalid srcByteCount, width:"
1421 << size.width << ", height:" << size.height;
1422 return {};
1424 const auto dstByteCount = dest.length();
1425 if (srcByteCount.value() > dstByteCount) {
1426 gfxCriticalError() << "SnapshotInto: srcByteCount:" << srcByteCount.value()
1427 << " > dstByteCount:" << dstByteCount;
1428 return {};
1430 uint8_t* dstPtr = dest.begin().get();
1431 gl->fReadPixels(0, 0, size.width, size.height, LOCAL_GL_RGBA,
1432 LOCAL_GL_UNSIGNED_BYTE, dstPtr);
1434 if (!IsWebGL2() && stride > minStride.value() && size.height > 1) {
1435 // WebGL 1 doesn't support PACK_ROW_LENGTH. Instead, we read the data tight
1436 // into the front of the buffer, and use memmove (since the source and dest
1437 // may overlap) starting from the back to move it to the correct stride
1438 // offsets. We don't move the first row as it is already in the right place.
1439 uint8_t* destRow = dstPtr + stride * (size.height - 1);
1440 const uint8_t* srcRow = dstPtr + minStride.value() * (size.height - 1);
1441 while (destRow > dstPtr) {
1442 memmove(destRow, srcRow, minStride.value());
1443 destRow -= stride;
1444 srcRow -= minStride.value();
1448 return Some(*uvec2::FromSize(size));
1451 void WebGLContext::ClearVRSwapChain() { mWebVRSwapChain.ClearPool(); }
1453 // ------------------------
1455 RefPtr<gfx::DataSourceSurface> GetTempSurface(const gfx::IntSize& aSize,
1456 gfx::SurfaceFormat& aFormat) {
1457 uint32_t stride =
1458 gfx::GetAlignedStride<8>(aSize.width, BytesPerPixel(aFormat));
1459 return gfx::Factory::CreateDataSourceSurfaceWithStride(aSize, aFormat,
1460 stride);
1463 void WebGLContext::DummyReadFramebufferOperation() {
1464 if (!mBoundReadFramebuffer) return; // Infallible.
1466 const auto status = mBoundReadFramebuffer->CheckFramebufferStatus();
1467 if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
1468 ErrorInvalidFramebufferOperation("Framebuffer must be complete.");
1472 dom::ContentParentId WebGLContext::GetContentId() const {
1473 const auto* outOfProcess = mHost ? mHost->mOwnerData.outOfProcess : nullptr;
1474 if (outOfProcess) {
1475 return outOfProcess->mContentId;
1477 if (XRE_IsContentProcess()) {
1478 return dom::ContentChild::GetSingleton()->GetID();
1480 return dom::ContentParentId();
1483 bool WebGLContext::Has64BitTimestamps() const {
1484 // 'sync' provides glGetInteger64v either by supporting ARB_sync, GL3+, or
1485 // GLES3+.
1486 return gl->IsSupported(gl::GLFeature::sync);
1489 static bool CheckContextLost(gl::GLContext* gl, bool* const out_isGuilty) {
1490 MOZ_ASSERT(gl);
1492 const auto resetStatus = gl->fGetGraphicsResetStatus();
1493 if (resetStatus == LOCAL_GL_NO_ERROR) {
1494 *out_isGuilty = false;
1495 return false;
1498 // Assume guilty unless we find otherwise!
1499 bool isGuilty = true;
1500 switch (resetStatus) {
1501 case LOCAL_GL_INNOCENT_CONTEXT_RESET_ARB:
1502 case LOCAL_GL_PURGED_CONTEXT_RESET_NV:
1503 // Either nothing wrong, or not our fault.
1504 isGuilty = false;
1505 break;
1506 case LOCAL_GL_GUILTY_CONTEXT_RESET_ARB:
1507 NS_WARNING(
1508 "WebGL content on the page definitely caused the graphics"
1509 " card to reset.");
1510 break;
1511 case LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB:
1512 NS_WARNING(
1513 "WebGL content on the page might have caused the graphics"
1514 " card to reset");
1515 // If we can't tell, assume not-guilty.
1516 // Todo: Implement max number of "unknown" resets per document or time.
1517 isGuilty = false;
1518 break;
1519 default:
1520 gfxCriticalError() << "Unexpected glGetGraphicsResetStatus: "
1521 << gfx::hexa(resetStatus);
1522 break;
1525 if (isGuilty) {
1526 NS_WARNING(
1527 "WebGL context on this page is considered guilty, and will"
1528 " not be restored.");
1531 *out_isGuilty = isGuilty;
1532 return true;
1535 void WebGLContext::RunContextLossTimer() { mContextLossHandler.RunTimer(); }
1537 // We use this timer for many things. Here are the things that it is activated
1538 // for:
1539 // 1) If a script is using the MOZ_WEBGL_lose_context extension.
1540 // 2) If we are using EGL and _NOT ANGLE_, we query periodically to see if the
1541 // CONTEXT_LOST_WEBGL error has been triggered.
1542 // 3) If we are using ANGLE, or anything that supports ARB_robustness, query the
1543 // GPU periodically to see if the reset status bit has been set.
1544 // In all of these situations, we use this timer to send the script context lost
1545 // and restored events asynchronously. For example, if it triggers a context
1546 // loss, the webglcontextlost event will be sent to it the next time the
1547 // robustness timer fires.
1548 // Note that this timer mechanism is not used unless one of these 3 criteria are
1549 // met.
1550 // At a bare minimum, from context lost to context restores, it would take 3
1551 // full timer iterations: detection, webglcontextlost, webglcontextrestored.
1552 void WebGLContext::CheckForContextLoss() {
1553 bool isGuilty = true;
1554 const auto isContextLost = CheckContextLost(gl, &isGuilty);
1555 if (!isContextLost) return;
1557 mWebGLError = LOCAL_GL_CONTEXT_LOST;
1559 auto reason = webgl::ContextLossReason::None;
1560 if (isGuilty) {
1561 reason = webgl::ContextLossReason::Guilty;
1563 LoseContext(reason);
1566 void WebGLContext::HandlePendingContextLoss() {
1567 mIsContextLost = true;
1568 if (mHost) {
1569 mHost->OnContextLoss(mPendingContextLossReason);
1573 void WebGLContext::LoseContextLruLocked(const webgl::ContextLossReason reason) {
1574 printf_stderr("WebGL(%p)::LoseContext(%u)\n", this,
1575 static_cast<uint32_t>(reason));
1576 mLruPosition.ResetLocked();
1577 mPendingContextLossReason = reason;
1578 mPendingContextLoss = true;
1581 void WebGLContext::LoseContext(const webgl::ContextLossReason reason) {
1582 StaticMutexAutoLock lock(sLruMutex);
1583 LoseContextLruLocked(reason);
1584 HandlePendingContextLoss();
1585 if (mRemoteTextureOwner) {
1586 mRemoteTextureOwner->NotifyContextLost();
1590 void WebGLContext::DidRefresh() {
1591 if (gl) {
1592 gl->FlushIfHeavyGLCallsSinceLastFlush();
1596 ////////////////////////////////////////////////////////////////////////////////
1598 uvec2 WebGLContext::DrawingBufferSize() {
1599 const FuncScope funcScope(*this, "width/height");
1600 if (IsContextLost()) return {};
1602 if (!EnsureDefaultFB()) return {};
1604 return *uvec2::FromSize(mDefaultFB->mSize);
1607 bool WebGLContext::ValidateAndInitFB(const WebGLFramebuffer* const fb,
1608 const GLenum incompleteFbError) {
1609 if (fb) return fb->ValidateAndInitAttachments(incompleteFbError);
1611 if (!EnsureDefaultFB()) return false;
1613 if (mDefaultFB_IsInvalid) {
1614 // Clear it!
1615 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mDefaultFB->mFB);
1616 const webgl::ScopedPrepForResourceClear scopedPrep(*this);
1617 if (!mOptions.alpha) {
1618 gl->fClearColor(0, 0, 0, 1);
1620 const GLbitfield bits = LOCAL_GL_COLOR_BUFFER_BIT |
1621 LOCAL_GL_DEPTH_BUFFER_BIT |
1622 LOCAL_GL_STENCIL_BUFFER_BIT;
1623 gl->fClear(bits);
1625 mDefaultFB_IsInvalid = false;
1627 return true;
1630 void WebGLContext::DoBindFB(const WebGLFramebuffer* const fb,
1631 const GLenum target) const {
1632 const GLenum driverFB = fb ? fb->mGLName : mDefaultFB->mFB;
1633 gl->fBindFramebuffer(target, driverFB);
1636 bool WebGLContext::BindCurFBForDraw() {
1637 const auto& fb = mBoundDrawFramebuffer;
1638 if (!ValidateAndInitFB(fb)) return false;
1640 DoBindFB(fb);
1641 return true;
1644 bool WebGLContext::BindCurFBForColorRead(
1645 const webgl::FormatUsageInfo** const out_format, uint32_t* const out_width,
1646 uint32_t* const out_height, const GLenum incompleteFbError) {
1647 const auto& fb = mBoundReadFramebuffer;
1649 if (fb) {
1650 if (!ValidateAndInitFB(fb, incompleteFbError)) return false;
1651 if (!fb->ValidateForColorRead(out_format, out_width, out_height))
1652 return false;
1654 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fb->mGLName);
1655 return true;
1658 if (!BindDefaultFBForRead()) return false;
1660 if (mDefaultFB_ReadBuffer == LOCAL_GL_NONE) {
1661 ErrorInvalidOperation(
1662 "Can't read from backbuffer when readBuffer mode is NONE.");
1663 return false;
1666 auto effFormat = mOptions.alpha ? webgl::EffectiveFormat::RGBA8
1667 : webgl::EffectiveFormat::RGB8;
1669 *out_format = mFormatUsage->GetUsage(effFormat);
1670 MOZ_ASSERT(*out_format);
1672 *out_width = mDefaultFB->mSize.width;
1673 *out_height = mDefaultFB->mSize.height;
1674 return true;
1677 bool WebGLContext::BindDefaultFBForRead() {
1678 if (!ValidateAndInitFB(nullptr)) return false;
1680 if (!mDefaultFB->mSamples) {
1681 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mDefaultFB->mFB);
1682 return true;
1685 if (!mResolvedDefaultFB) {
1686 mResolvedDefaultFB =
1687 gl::MozFramebuffer::Create(gl, mDefaultFB->mSize, 0, false);
1688 if (!mResolvedDefaultFB) {
1689 gfxCriticalNote << FuncName() << ": Failed to create mResolvedDefaultFB.";
1690 return false;
1694 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mResolvedDefaultFB->mFB);
1695 BlitBackbufferToCurDriverFB();
1697 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mResolvedDefaultFB->mFB);
1698 return true;
1701 void WebGLContext::DoColorMask(Maybe<GLuint> i, const uint8_t bitmask) const {
1702 if (!IsExtensionEnabled(WebGLExtensionID::OES_draw_buffers_indexed)) {
1703 i = Nothing();
1705 const auto bs = std::bitset<4>(bitmask);
1706 if (i) {
1707 gl->fColorMaski(*i, bs[0], bs[1], bs[2], bs[3]);
1708 } else {
1709 gl->fColorMask(bs[0], bs[1], bs[2], bs[3]);
1713 ////////////////////////////////////////////////////////////////////////////////
1715 ScopedDrawCallWrapper::ScopedDrawCallWrapper(WebGLContext& webgl)
1716 : mWebGL(webgl) {
1717 uint8_t driverColorMask0 = mWebGL.mColorWriteMask0;
1718 bool driverDepthTest = mWebGL.mDepthTestEnabled;
1719 bool driverStencilTest = mWebGL.mStencilTestEnabled;
1720 const auto& fb = mWebGL.mBoundDrawFramebuffer;
1721 if (!fb) {
1722 if (mWebGL.mDefaultFB_DrawBuffer0 == LOCAL_GL_NONE) {
1723 driverColorMask0 = 0; // Is this well-optimized enough for depth-first
1724 // rendering?
1725 } else {
1726 driverColorMask0 &= ~(uint8_t(mWebGL.mNeedsFakeNoAlpha) << 3);
1728 driverDepthTest &= !mWebGL.mNeedsFakeNoDepth;
1729 driverStencilTest &= !mWebGL.mNeedsFakeNoStencil;
1732 const auto& gl = mWebGL.gl;
1733 mWebGL.DoColorMask(Some(0), driverColorMask0);
1734 if (mWebGL.mDriverDepthTest != driverDepthTest) {
1735 // "When disabled, the depth comparison and subsequent possible updates to
1736 // the
1737 // depth buffer value are bypassed and the fragment is passed to the next
1738 // operation." [GLES 3.0.5, p177]
1739 mWebGL.mDriverDepthTest = driverDepthTest;
1740 gl->SetEnabled(LOCAL_GL_DEPTH_TEST, mWebGL.mDriverDepthTest);
1742 if (mWebGL.mDriverStencilTest != driverStencilTest) {
1743 // "When disabled, the stencil test and associated modifications are not
1744 // made, and
1745 // the fragment is always passed." [GLES 3.0.5, p175]
1746 mWebGL.mDriverStencilTest = driverStencilTest;
1747 gl->SetEnabled(LOCAL_GL_STENCIL_TEST, mWebGL.mDriverStencilTest);
1751 ScopedDrawCallWrapper::~ScopedDrawCallWrapper() {
1752 if (mWebGL.mBoundDrawFramebuffer) return;
1754 mWebGL.mResolvedDefaultFB = nullptr;
1755 mWebGL.mShouldPresent = true;
1758 // -
1760 void WebGLContext::ScissorRect::Apply(gl::GLContext& gl) const {
1761 gl.fScissor(x, y, w, h);
1764 ////////////////////////////////////////
1766 IndexedBufferBinding::IndexedBufferBinding() = default;
1767 IndexedBufferBinding::~IndexedBufferBinding() = default;
1769 uint64_t IndexedBufferBinding::ByteCount() const {
1770 if (!mBufferBinding) return 0;
1772 uint64_t bufferSize = mBufferBinding->ByteLength();
1773 if (!mRangeSize) // BindBufferBase
1774 return bufferSize;
1776 if (mRangeStart >= bufferSize) return 0;
1777 bufferSize -= mRangeStart;
1779 return std::min(bufferSize, mRangeSize);
1782 ////////////////////////////////////////
1784 ScopedFBRebinder::~ScopedFBRebinder() {
1785 const auto fnName = [&](WebGLFramebuffer* fb) {
1786 return fb ? fb->mGLName : 0;
1789 const auto& gl = mWebGL->gl;
1790 if (mWebGL->IsWebGL2()) {
1791 gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER,
1792 fnName(mWebGL->mBoundDrawFramebuffer));
1793 gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER,
1794 fnName(mWebGL->mBoundReadFramebuffer));
1795 } else {
1796 MOZ_ASSERT(mWebGL->mBoundDrawFramebuffer == mWebGL->mBoundReadFramebuffer);
1797 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER,
1798 fnName(mWebGL->mBoundDrawFramebuffer));
1802 ////////////////////
1804 void DoBindBuffer(gl::GLContext& gl, const GLenum target,
1805 const WebGLBuffer* const buffer) {
1806 gl.fBindBuffer(target, buffer ? buffer->mGLName : 0);
1809 ////////////////////////////////////////
1811 bool Intersect(const int32_t srcSize, const int32_t read0,
1812 const int32_t readSize, int32_t* const out_intRead0,
1813 int32_t* const out_intWrite0, int32_t* const out_intSize) {
1814 MOZ_ASSERT(srcSize >= 0);
1815 MOZ_ASSERT(readSize >= 0);
1816 const auto read1 = int64_t(read0) + readSize;
1818 int32_t intRead0 = read0; // Clearly doesn't need validation.
1819 int64_t intWrite0 = 0;
1820 int64_t intSize = readSize;
1822 if (read1 <= 0 || read0 >= srcSize) {
1823 // Disjoint ranges.
1824 intSize = 0;
1825 } else {
1826 if (read0 < 0) {
1827 const auto diff = int64_t(0) - read0;
1828 MOZ_ASSERT(diff >= 0);
1829 intRead0 = 0;
1830 intWrite0 = diff;
1831 intSize -= diff;
1833 if (read1 > srcSize) {
1834 const auto diff = int64_t(read1) - srcSize;
1835 MOZ_ASSERT(diff >= 0);
1836 intSize -= diff;
1839 if (!CheckedInt<int32_t>(intWrite0).isValid() ||
1840 !CheckedInt<int32_t>(intSize).isValid()) {
1841 return false;
1845 *out_intRead0 = intRead0;
1846 *out_intWrite0 = intWrite0;
1847 *out_intSize = intSize;
1848 return true;
1851 // --
1853 uint64_t AvailGroups(const uint64_t totalAvailItems,
1854 const uint64_t firstItemOffset, const uint32_t groupSize,
1855 const uint32_t groupStride) {
1856 MOZ_ASSERT(groupSize && groupStride);
1857 MOZ_ASSERT(groupSize <= groupStride);
1859 if (totalAvailItems <= firstItemOffset) return 0;
1860 const size_t availItems = totalAvailItems - firstItemOffset;
1862 size_t availGroups = availItems / groupStride;
1863 const size_t tailItems = availItems % groupStride;
1864 if (tailItems >= groupSize) {
1865 availGroups += 1;
1867 return availGroups;
1870 ////////////////////////////////////////////////////////////////////////////////
1872 const char* WebGLContext::FuncName() const {
1873 const char* ret;
1874 if (MOZ_LIKELY(mFuncScope)) {
1875 ret = mFuncScope->mFuncName;
1876 } else {
1877 NS_WARNING("FuncScope not on stack!");
1878 ret = "<unknown function>";
1880 return ret;
1883 // -
1885 WebGLContext::FuncScope::FuncScope(const WebGLContext& webgl,
1886 const char* const funcName)
1887 : mWebGL(webgl), mFuncName(bool(mWebGL.mFuncScope) ? nullptr : funcName) {
1888 if (!mFuncName) return;
1889 mWebGL.mFuncScope = this;
1892 WebGLContext::FuncScope::~FuncScope() {
1893 if (mBindFailureGuard) {
1894 gfxCriticalError() << "mBindFailureGuard failure: Early exit from "
1895 << mWebGL.FuncName();
1898 if (!mFuncName) return;
1899 mWebGL.mFuncScope = nullptr;
1902 // --
1904 bool ClientWebGLContext::IsXRCompatible() const { return mXRCompatible; }
1906 already_AddRefed<dom::Promise> ClientWebGLContext::MakeXRCompatible(
1907 ErrorResult& aRv) {
1908 const FuncScope funcScope(*this, "MakeXRCompatible");
1909 nsCOMPtr<nsIGlobalObject> global = GetParentObject();
1910 if (!global) {
1911 aRv.ThrowInvalidAccessError(
1912 "Using a WebGL context that is not attached to either a canvas or an "
1913 "OffscreenCanvas");
1914 return nullptr;
1916 RefPtr<dom::Promise> promise = dom::Promise::Create(global, aRv);
1917 NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
1919 if (IsContextLost()) {
1920 promise->MaybeRejectWithInvalidStateError(
1921 "Can not make context XR compatible when context is already lost.");
1922 return promise.forget();
1925 // TODO: Bug 1580258 - WebGLContext.MakeXRCompatible needs to switch to
1926 // the device connected to the XR hardware
1927 // This should update `options` and lose+restore the context.
1928 mXRCompatible = true;
1929 promise->MaybeResolveWithUndefined();
1930 return promise.forget();
1933 // --
1935 webgl::AvailabilityRunnable& ClientWebGLContext::EnsureAvailabilityRunnable()
1936 const {
1937 if (!mAvailabilityRunnable) {
1938 mAvailabilityRunnable = new webgl::AvailabilityRunnable(this);
1939 auto forgettable = mAvailabilityRunnable;
1940 NS_DispatchToCurrentThread(forgettable.forget());
1942 return *mAvailabilityRunnable;
1945 webgl::AvailabilityRunnable::AvailabilityRunnable(
1946 const ClientWebGLContext* const webgl)
1947 : DiscardableRunnable("webgl::AvailabilityRunnable"), mWebGL(webgl) {}
1949 webgl::AvailabilityRunnable::~AvailabilityRunnable() {
1950 MOZ_ASSERT(mQueries.empty());
1951 MOZ_ASSERT(mSyncs.empty());
1954 nsresult webgl::AvailabilityRunnable::Run() {
1955 for (const auto& cur : mQueries) {
1956 if (!cur) continue;
1957 cur->mCanBeAvailable = true;
1959 mQueries.clear();
1961 for (const auto& cur : mSyncs) {
1962 if (!cur) continue;
1963 cur->mCanBeAvailable = true;
1965 mSyncs.clear();
1967 if (mWebGL) {
1968 mWebGL->mAvailabilityRunnable = nullptr;
1970 return NS_OK;
1973 // -
1975 void WebGLContext::JsWarning(const std::string& text) const {
1976 if (mHost) {
1977 mHost->JsWarning(text);
1979 #ifdef DEBUG
1980 if (!mHost) {
1981 NS_WARNING(text.c_str());
1983 #endif
1986 void WebGLContext::GenerateErrorImpl(const GLenum errOrWarning,
1987 const std::string& text) const {
1988 auto err = errOrWarning;
1989 bool isPerfWarning = false;
1990 if (err == webgl::kErrorPerfWarning) {
1991 err = 0;
1992 isPerfWarning = true;
1995 if (err && mFuncScope && mFuncScope->mBindFailureGuard) {
1996 gfxCriticalError() << "mBindFailureGuard failure: Generating error "
1997 << EnumString(err) << ": " << text;
2000 /* ES2 section 2.5 "GL Errors" states that implementations can have
2001 * multiple 'flags', as errors might be caught in different parts of
2002 * a distributed implementation.
2003 * We're signing up as a distributed implementation here, with
2004 * separate flags for WebGL and the underlying GLContext.
2006 if (!mWebGLError) mWebGLError = err;
2008 if (!mHost) return; // Impossible?
2010 // -
2012 const auto ShouldWarn = [&]() {
2013 if (isPerfWarning) {
2014 return ShouldGeneratePerfWarnings();
2016 return ShouldGenerateWarnings();
2018 if (!ShouldWarn()) return;
2020 // -
2022 auto* pNumWarnings = &mWarningCount;
2023 const char* warningsType = "warnings";
2024 if (isPerfWarning) {
2025 pNumWarnings = &mNumPerfWarnings;
2026 warningsType = "perf warnings";
2029 if (isPerfWarning) {
2030 const auto perfText = std::string("WebGL perf warning: ") + text;
2031 JsWarning(perfText);
2032 } else {
2033 JsWarning(text);
2035 *pNumWarnings += 1;
2037 if (!ShouldWarn()) {
2038 const auto& msg = nsPrintfCString(
2039 "After reporting %i, no further %s will be reported for this WebGL "
2040 "context.",
2041 int(*pNumWarnings), warningsType);
2042 JsWarning(ToString(msg));
2046 // -
2048 Maybe<std::string> WebGLContext::GetString(const GLenum pname) const {
2049 const WebGLContext::FuncScope funcScope(*this, "getParameter");
2050 if (IsContextLost()) return {};
2052 const auto FromRaw = [](const char* const raw) -> Maybe<std::string> {
2053 if (!raw) return {};
2054 return Some(std::string(raw));
2057 switch (pname) {
2058 case LOCAL_GL_EXTENSIONS: {
2059 if (!gl->IsCoreProfile()) {
2060 const auto rawExt = (const char*)gl->fGetString(LOCAL_GL_EXTENSIONS);
2061 return FromRaw(rawExt);
2063 std::string ret;
2064 const auto& numExts = gl->GetIntAs<GLuint>(LOCAL_GL_NUM_EXTENSIONS);
2065 for (GLuint i = 0; i < numExts; i++) {
2066 const auto rawExt =
2067 (const char*)gl->fGetStringi(LOCAL_GL_EXTENSIONS, i);
2068 if (!rawExt) continue;
2070 if (i > 0) {
2071 ret += " ";
2073 ret += rawExt;
2075 return Some(std::move(ret));
2078 case LOCAL_GL_RENDERER:
2079 case LOCAL_GL_VENDOR:
2080 case LOCAL_GL_VERSION: {
2081 const auto raw = (const char*)gl->fGetString(pname);
2082 return FromRaw(raw);
2085 case dom::MOZ_debug_Binding::WSI_INFO: {
2086 nsCString info;
2087 gl->GetWSIInfo(&info);
2088 return Some(std::string(info.BeginReading()));
2091 default:
2092 ErrorInvalidEnumArg("pname", pname);
2093 return {};
2097 // ---------------------------------
2099 Maybe<webgl::IndexedName> webgl::ParseIndexed(const std::string& str) {
2100 static const std::regex kRegex("(.*)\\[([0-9]+)\\]");
2102 std::smatch match;
2103 if (!std::regex_match(str, match, kRegex)) return {};
2105 const auto index = std::stoull(match[2]);
2106 return Some(webgl::IndexedName{match[1], index});
2109 // ExplodeName("foo.bar[3].x") -> ["foo", ".", "bar", "[", "3", "]", ".", "x"]
2110 static std::vector<std::string> ExplodeName(const std::string& str) {
2111 std::vector<std::string> ret;
2113 static const std::regex kSep("[.[\\]]");
2115 auto itr = std::regex_token_iterator<decltype(str.begin())>(
2116 str.begin(), str.end(), kSep, {-1, 0});
2117 const auto end = decltype(itr)();
2119 for (; itr != end; ++itr) {
2120 const auto& part = itr->str();
2121 if (part.size()) {
2122 ret.push_back(part);
2125 return ret;
2130 // #define DUMP_MakeLinkResult
2132 webgl::LinkActiveInfo GetLinkActiveInfo(
2133 gl::GLContext& gl, const GLuint prog, const bool webgl2,
2134 const std::unordered_map<std::string, std::string>& nameUnmap) {
2135 webgl::LinkActiveInfo ret;
2136 [&]() {
2137 const auto fnGetProgramui = [&](const GLenum pname) {
2138 GLint ret = 0;
2139 gl.fGetProgramiv(prog, pname, &ret);
2140 return static_cast<uint32_t>(ret);
2143 std::vector<char> stringBuffer(1);
2144 const auto fnEnsureCapacity = [&](const GLenum pname) {
2145 const auto maxWithNull = fnGetProgramui(pname);
2146 if (maxWithNull > stringBuffer.size()) {
2147 stringBuffer.resize(maxWithNull);
2151 fnEnsureCapacity(LOCAL_GL_ACTIVE_ATTRIBUTE_MAX_LENGTH);
2152 fnEnsureCapacity(LOCAL_GL_ACTIVE_UNIFORM_MAX_LENGTH);
2153 if (webgl2) {
2154 fnEnsureCapacity(LOCAL_GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH);
2155 fnEnsureCapacity(LOCAL_GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH);
2158 // -
2160 const auto fnUnmapName = [&](const std::string& mappedName) {
2161 const auto parts = ExplodeName(mappedName);
2163 std::ostringstream ret;
2164 for (const auto& part : parts) {
2165 const auto maybe = MaybeFind(nameUnmap, part);
2166 if (maybe) {
2167 ret << *maybe;
2168 } else {
2169 ret << part;
2172 return ret.str();
2175 // -
2178 const auto count = fnGetProgramui(LOCAL_GL_ACTIVE_ATTRIBUTES);
2179 ret.activeAttribs.reserve(count);
2180 for (const auto i : IntegerRange(count)) {
2181 GLsizei lengthWithoutNull = 0;
2182 GLint elemCount = 0; // `size`
2183 GLenum elemType = 0; // `type`
2184 gl.fGetActiveAttrib(prog, i, stringBuffer.size(), &lengthWithoutNull,
2185 &elemCount, &elemType, stringBuffer.data());
2186 if (!elemType) {
2187 const auto error = gl.fGetError();
2188 if (error != LOCAL_GL_CONTEXT_LOST) {
2189 gfxCriticalError() << "Failed to do glGetActiveAttrib: " << error;
2191 return;
2193 const auto mappedName =
2194 std::string(stringBuffer.data(), lengthWithoutNull);
2195 const auto userName = fnUnmapName(mappedName);
2197 auto loc = gl.fGetAttribLocation(prog, mappedName.c_str());
2198 if (mappedName.find("gl_") == 0) {
2199 // Bug 1328559: Appears problematic on ANGLE and OSX, but not Linux or
2200 // Win+GL.
2201 loc = -1;
2204 #ifdef DUMP_MakeLinkResult
2205 printf_stderr("[attrib %u/%u] @%i %s->%s\n", i, count, loc,
2206 userName.c_str(), mappedName.c_str());
2207 #endif
2208 webgl::ActiveAttribInfo info;
2209 info.elemType = elemType;
2210 info.elemCount = elemCount;
2211 info.name = userName;
2212 info.location = loc;
2213 info.baseType = webgl::ToAttribBaseType(info.elemType);
2214 ret.activeAttribs.push_back(std::move(info));
2218 // -
2221 const auto count = fnGetProgramui(LOCAL_GL_ACTIVE_UNIFORMS);
2222 ret.activeUniforms.reserve(count);
2224 std::vector<GLint> blockIndexList(count, -1);
2225 std::vector<GLint> blockOffsetList(count, -1);
2226 std::vector<GLint> blockArrayStrideList(count, -1);
2227 std::vector<GLint> blockMatrixStrideList(count, -1);
2228 std::vector<GLint> blockIsRowMajorList(count, 0);
2230 if (webgl2 && count) {
2231 std::vector<GLuint> activeIndices;
2232 activeIndices.reserve(count);
2233 for (const auto i : IntegerRange(count)) {
2234 activeIndices.push_back(i);
2237 gl.fGetActiveUniformsiv(
2238 prog, activeIndices.size(), activeIndices.data(),
2239 LOCAL_GL_UNIFORM_BLOCK_INDEX, blockIndexList.data());
2241 gl.fGetActiveUniformsiv(prog, activeIndices.size(),
2242 activeIndices.data(), LOCAL_GL_UNIFORM_OFFSET,
2243 blockOffsetList.data());
2245 gl.fGetActiveUniformsiv(
2246 prog, activeIndices.size(), activeIndices.data(),
2247 LOCAL_GL_UNIFORM_ARRAY_STRIDE, blockArrayStrideList.data());
2249 gl.fGetActiveUniformsiv(
2250 prog, activeIndices.size(), activeIndices.data(),
2251 LOCAL_GL_UNIFORM_MATRIX_STRIDE, blockMatrixStrideList.data());
2253 gl.fGetActiveUniformsiv(
2254 prog, activeIndices.size(), activeIndices.data(),
2255 LOCAL_GL_UNIFORM_IS_ROW_MAJOR, blockIsRowMajorList.data());
2258 for (const auto i : IntegerRange(count)) {
2259 GLsizei lengthWithoutNull = 0;
2260 GLint elemCount = 0; // `size`
2261 GLenum elemType = 0; // `type`
2262 gl.fGetActiveUniform(prog, i, stringBuffer.size(), &lengthWithoutNull,
2263 &elemCount, &elemType, stringBuffer.data());
2264 if (!elemType) {
2265 const auto error = gl.fGetError();
2266 if (error != LOCAL_GL_CONTEXT_LOST) {
2267 gfxCriticalError() << "Failed to do glGetActiveUniform: " << error;
2269 return;
2271 auto mappedName = std::string(stringBuffer.data(), lengthWithoutNull);
2273 // Get true name
2275 auto baseMappedName = mappedName;
2277 const bool isArray = [&]() {
2278 const auto maybe = webgl::ParseIndexed(mappedName);
2279 if (maybe) {
2280 MOZ_ASSERT(maybe->index == 0);
2281 baseMappedName = std::move(maybe->name);
2282 return true;
2284 return false;
2285 }();
2287 const auto userName = fnUnmapName(mappedName);
2288 if (StartsWith(userName, "webgl_")) continue;
2290 // -
2292 webgl::ActiveUniformInfo info;
2293 info.elemType = elemType;
2294 info.elemCount = static_cast<uint32_t>(elemCount);
2295 info.name = userName;
2296 info.block_index = blockIndexList[i];
2297 info.block_offset = blockOffsetList[i];
2298 info.block_arrayStride = blockArrayStrideList[i];
2299 info.block_matrixStride = blockMatrixStrideList[i];
2300 info.block_isRowMajor = bool(blockIsRowMajorList[i]);
2302 #ifdef DUMP_MakeLinkResult
2303 printf_stderr("[uniform %u/%u] %s->%s\n", i + 1, count,
2304 userName.c_str(), mappedName.c_str());
2305 #endif
2307 // Get uniform locations
2309 auto locName = baseMappedName;
2310 const auto baseLength = locName.size();
2311 for (const auto i : IntegerRange(info.elemCount)) {
2312 if (isArray) {
2313 locName.erase(
2314 baseLength); // Erase previous [N], but retain capacity.
2315 locName += '[';
2316 locName += std::to_string(i);
2317 locName += ']';
2319 const auto loc = gl.fGetUniformLocation(prog, locName.c_str());
2320 if (loc != -1) {
2321 info.locByIndex[i] = static_cast<uint32_t>(loc);
2322 #ifdef DUMP_MakeLinkResult
2323 printf_stderr(" [%u] @%i\n", i, loc);
2324 #endif
2327 } // anon
2329 ret.activeUniforms.push_back(std::move(info));
2330 } // for i
2331 } // anon
2333 if (webgl2) {
2334 // -------------------------------------
2335 // active uniform blocks
2337 const auto count = fnGetProgramui(LOCAL_GL_ACTIVE_UNIFORM_BLOCKS);
2338 ret.activeUniformBlocks.reserve(count);
2340 for (const auto i : IntegerRange(count)) {
2341 GLsizei lengthWithoutNull = 0;
2342 gl.fGetActiveUniformBlockName(prog, i, stringBuffer.size(),
2343 &lengthWithoutNull,
2344 stringBuffer.data());
2345 const auto mappedName =
2346 std::string(stringBuffer.data(), lengthWithoutNull);
2347 const auto userName = fnUnmapName(mappedName);
2349 // -
2351 auto info = webgl::ActiveUniformBlockInfo{userName};
2352 GLint val = 0;
2354 gl.fGetActiveUniformBlockiv(prog, i, LOCAL_GL_UNIFORM_BLOCK_DATA_SIZE,
2355 &val);
2356 info.dataSize = static_cast<uint32_t>(val);
2358 gl.fGetActiveUniformBlockiv(
2359 prog, i, LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &val);
2360 info.activeUniformIndices.resize(val);
2361 gl.fGetActiveUniformBlockiv(
2362 prog, i, LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES,
2363 reinterpret_cast<GLint*>(info.activeUniformIndices.data()));
2365 gl.fGetActiveUniformBlockiv(
2366 prog, i, LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER,
2367 &val);
2368 info.referencedByVertexShader = bool(val);
2370 gl.fGetActiveUniformBlockiv(
2371 prog, i, LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER,
2372 &val);
2373 info.referencedByFragmentShader = bool(val);
2375 ret.activeUniformBlocks.push_back(std::move(info));
2376 } // for i
2377 } // anon
2379 // -------------------------------------
2380 // active tf varyings
2382 const auto count = fnGetProgramui(LOCAL_GL_TRANSFORM_FEEDBACK_VARYINGS);
2383 ret.activeTfVaryings.reserve(count);
2385 for (const auto i : IntegerRange(count)) {
2386 GLsizei lengthWithoutNull = 0;
2387 GLsizei elemCount = 0; // `size`
2388 GLenum elemType = 0; // `type`
2389 gl.fGetTransformFeedbackVarying(prog, i, stringBuffer.size(),
2390 &lengthWithoutNull, &elemCount,
2391 &elemType, stringBuffer.data());
2392 const auto mappedName =
2393 std::string(stringBuffer.data(), lengthWithoutNull);
2394 const auto userName = fnUnmapName(mappedName);
2396 ret.activeTfVaryings.push_back(
2397 {elemType, static_cast<uint32_t>(elemCount), userName});
2400 } // if webgl2
2401 }();
2402 return ret;
2405 nsCString ToCString(const std::string& s) {
2406 return nsCString(s.data(), s.size());
2409 webgl::CompileResult WebGLContext::GetCompileResult(
2410 const WebGLShader& shader) const {
2411 webgl::CompileResult ret;
2412 [&]() {
2413 ret.pending = false;
2414 const auto& info = shader.CompileResults();
2415 if (!info) return;
2416 if (!info->mValid) {
2417 ret.log = info->mInfoLog.c_str();
2418 return;
2420 // TODO: These could be large and should be made fallible.
2421 ret.translatedSource = ToCString(info->mObjectCode);
2422 ret.log = ToCString(shader.CompileLog());
2423 if (!shader.IsCompiled()) return;
2424 ret.success = true;
2425 }();
2426 return ret;
2429 webgl::LinkResult WebGLContext::GetLinkResult(const WebGLProgram& prog) const {
2430 webgl::LinkResult ret;
2431 [&]() {
2432 ret.pending = false; // Link status polling not yet implemented.
2433 ret.log = ToCString(prog.LinkLog());
2434 const auto& info = prog.LinkInfo();
2435 if (!info) return;
2436 ret.success = true;
2437 ret.active = info->active;
2438 ret.tfBufferMode = info->transformFeedbackBufferMode;
2439 }();
2440 return ret;
2443 // -
2445 GLint WebGLContext::GetFragDataLocation(const WebGLProgram& prog,
2446 const std::string& userName) const {
2447 const auto err = CheckGLSLVariableName(IsWebGL2(), userName);
2448 if (err) {
2449 GenerateError(err->type, "%s", err->info.c_str());
2450 return -1;
2453 const auto& info = prog.LinkInfo();
2454 if (!info) return -1;
2455 const auto& nameMap = info->nameMap;
2457 const auto parts = ExplodeName(userName);
2459 std::ostringstream ret;
2460 for (const auto& part : parts) {
2461 const auto maybe = MaybeFind(nameMap, part);
2462 if (maybe) {
2463 ret << *maybe;
2464 } else {
2465 ret << part;
2468 const auto mappedName = ret.str();
2470 if (gl->WorkAroundDriverBugs() && gl->IsMesa()) {
2471 // Mesa incorrectly generates INVALID_OPERATION for gl_ prefixes here.
2472 if (mappedName.find("gl_") == 0) {
2473 return -1;
2477 return gl->fGetFragDataLocation(prog.mGLName, mappedName.c_str());
2480 // -
2482 WebGLContextBoundObject::WebGLContextBoundObject(WebGLContext* webgl)
2483 : mContext(webgl) {}
2485 // -
2487 Result<webgl::ExplicitPixelPackingState, std::string>
2488 webgl::ExplicitPixelPackingState::ForUseWith(
2489 const webgl::PixelPackingState& stateOrZero, const GLenum target,
2490 const uvec3& subrectSize, const webgl::PackingInfo& pi,
2491 const Maybe<size_t> bytesPerRowStrideOverride) {
2492 auto state = stateOrZero;
2494 if (!IsTexTarget3D(target)) {
2495 state.skipImages = 0;
2496 state.imageHeight = 0;
2498 if (!state.rowLength) {
2499 state.rowLength = subrectSize.x;
2501 if (!state.imageHeight) {
2502 state.imageHeight = subrectSize.y;
2505 // -
2507 const auto mpii = PackingInfoInfo::For(pi);
2508 if (!mpii) {
2509 const auto text =
2510 nsPrintfCString("Invalid pi: { 0x%x, 0x%x}", pi.format, pi.type);
2511 return Err(mozilla::ToString(text));
2513 const auto pii = *mpii;
2514 const auto bytesPerPixel = pii.BytesPerPixel();
2516 const auto ElemsPerRowStride = [&]() {
2517 // GLES 3.0.6 p116:
2518 // p: `Elem*` pointer to the first element of the first row
2519 // N: row number, starting at 0
2520 // l: groups (pixels) per row
2521 // n: elements per group (pixel) in [1,2,3,4]
2522 // s: bytes per element in [1,2,4,8]
2523 // a: UNPACK_ALIGNMENT in [1,2,4,8]
2524 // Pointer to first element of Nth row: p + N*k
2525 // k(s>=a): n*l
2526 // k(s<a): a/s * ceil(s*n*l/a)
2527 const auto n__elemsPerPixel = pii.elementsPerPixel;
2528 const auto l__pixelsPerRow = state.rowLength;
2529 const auto a__alignment = state.alignmentInTypeElems;
2530 const auto s__bytesPerElem = pii.bytesPerElement;
2532 const auto nl = CheckedInt<size_t>(n__elemsPerPixel) * l__pixelsPerRow;
2533 auto k__elemsPerRowStride = nl;
2534 if (s__bytesPerElem < a__alignment) {
2535 // k = a/s * ceil(s*n*l/a)
2536 k__elemsPerRowStride =
2537 a__alignment / s__bytesPerElem *
2538 ((nl * s__bytesPerElem + a__alignment - 1) / a__alignment);
2540 return k__elemsPerRowStride;
2543 // -
2545 if (bytesPerRowStrideOverride) { // E.g. HTMLImageElement
2546 const size_t bytesPerRowStrideRequired = *bytesPerRowStrideOverride;
2547 // We have to reverse-engineer an ALIGNMENT and ROW_LENGTH for this.
2549 // GL does this in elems not bytes, so we should too.
2550 MOZ_RELEASE_ASSERT(bytesPerRowStrideRequired % pii.bytesPerElement == 0);
2551 const auto elemsPerRowStrideRequired =
2552 bytesPerRowStrideRequired / pii.bytesPerElement;
2554 state.rowLength = bytesPerRowStrideRequired / bytesPerPixel;
2555 state.alignmentInTypeElems = 8;
2556 while (true) {
2557 const auto elemPerRowStride = ElemsPerRowStride();
2558 if (elemPerRowStride.isValid() &&
2559 elemPerRowStride.value() == elemsPerRowStrideRequired) {
2560 break;
2562 state.alignmentInTypeElems /= 2;
2563 if (!state.alignmentInTypeElems) {
2564 const auto text = nsPrintfCString(
2565 "No valid alignment found: pi: { 0x%x, 0x%x},"
2566 " bytesPerRowStrideRequired: %zu",
2567 pi.format, pi.type, bytesPerRowStrideRequired);
2568 return Err(mozilla::ToString(text));
2573 // -
2575 const auto usedPixelsPerRow =
2576 CheckedInt<size_t>(state.skipPixels) + subrectSize.x;
2577 if (!usedPixelsPerRow.isValid() ||
2578 usedPixelsPerRow.value() > state.rowLength) {
2579 return Err("UNPACK_SKIP_PIXELS + width > UNPACK_ROW_LENGTH.");
2582 if (subrectSize.y > state.imageHeight) {
2583 return Err("height > UNPACK_IMAGE_HEIGHT.");
2585 // The spec doesn't bound SKIP_ROWS + height <= IMAGE_HEIGHT, unfortunately.
2587 // -
2589 auto metrics = Metrics{};
2591 metrics.usedSize = subrectSize;
2592 metrics.bytesPerPixel = BytesPerPixel(pi);
2594 // -
2596 const auto elemsPerRowStride = ElemsPerRowStride();
2597 const auto bytesPerRowStride = pii.bytesPerElement * elemsPerRowStride;
2598 if (!bytesPerRowStride.isValid()) {
2599 return Err("ROW_LENGTH or width too large for packing.");
2601 metrics.bytesPerRowStride = bytesPerRowStride.value();
2603 // -
2605 const auto firstImageTotalRows =
2606 CheckedInt<size_t>(state.skipRows) + metrics.usedSize.y;
2607 const auto totalImages =
2608 CheckedInt<size_t>(state.skipImages) + metrics.usedSize.z;
2609 auto totalRows = CheckedInt<size_t>(0);
2610 if (metrics.usedSize.y && metrics.usedSize.z) {
2611 totalRows = firstImageTotalRows + state.imageHeight * (totalImages - 1);
2613 if (!totalRows.isValid()) {
2614 return Err(
2615 "SKIP_ROWS, height, IMAGE_HEIGHT, SKIP_IMAGES, or depth too large for "
2616 "packing.");
2618 metrics.totalRows = totalRows.value();
2620 // -
2622 const auto totalBytesStrided = totalRows * metrics.bytesPerRowStride;
2623 if (!totalBytesStrided.isValid()) {
2624 return Err("Total byte count too large for packing.");
2626 metrics.totalBytesStrided = totalBytesStrided.value();
2628 metrics.totalBytesUsed = metrics.totalBytesStrided;
2629 if (metrics.usedSize.x && metrics.usedSize.y && metrics.usedSize.z) {
2630 const auto usedBytesPerRow =
2631 usedPixelsPerRow.value() * metrics.bytesPerPixel;
2632 metrics.totalBytesUsed -= metrics.bytesPerRowStride;
2633 metrics.totalBytesUsed += usedBytesPerRow;
2636 // -
2638 return {{state, metrics}};
2641 } // namespace mozilla