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"
13 #include "AccessCheck.h"
14 #include "CompositableHost.h"
15 #include "gfxConfig.h"
16 #include "gfxContext.h"
17 #include "gfxCrashReporterUtils.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"
50 #include "nsIClassInfoImpl.h"
51 #include "nsIWidget.h"
52 #include "nsServiceManagerUtils.h"
53 #include "SharedSurfaceGL.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"
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"
90 # include "WGLLibrary.h"
94 #include "mozilla/dom/WebGLRenderingContextBinding.h"
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
);
112 WebGLContext::LruPosition::LruPosition(WebGLContext
& context
) {
113 StaticMutexAutoLock
lock(sLruMutex
);
114 mItr
= sLru
.insert(sLru
.end(), &context
);
117 void WebGLContext::LruPosition::AssignLocked(WebGLContext
& aContext
) {
119 mItr
= sLru
.insert(sLru
.end(), &aContext
);
122 void WebGLContext::LruPosition::ResetLocked() {
123 const auto end
= sLru
.end();
130 void WebGLContext::LruPosition::Reset() {
131 StaticMutexAutoLock
lock(sLruMutex
);
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
143 mResistFingerprinting(desc
.resistFingerprinting
),
144 mOptions(desc
.options
),
145 mPrincipalKey(desc
.principalKey
),
146 mContextLossHandler(this),
147 mRequestedSize(desc
.size
) {
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;
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();
199 gl
->fDeleteTransformFeedbacks(1, &mEmptyTFO
);
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());
226 mGL_OnlyClearInDestroyResourcesAndContext
= nullptr;
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();
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()) {
268 "AllowWebgl2:false restricts context creation on this system.";
269 out_failReasons
->push_back(reason
);
270 GenerateWarning("%s", reason
.info
.BeginReading());
275 auto flags
= gl::CreateContextFlags::PREFER_ROBUSTNESS
;
277 if (StaticPrefs::webgl_gl_khr_no_error()) {
278 flags
|= gl::CreateContextFlags::NO_VALIDATION
;
283 if (StaticPrefs::webgl_forbid_hardware()) {
284 flags
|= gl::CreateContextFlags::FORBID_HARDWARE
;
286 if (StaticPrefs::webgl_forbid_software()) {
287 flags
|= gl::CreateContextFlags::FORBID_SOFTWARE
;
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());
306 if (StaticPrefs::webgl_cgl_multithreaded()) {
307 flags
|= gl::CreateContextFlags::PREFER_MULTITHREADED
;
311 flags
|= gl::CreateContextFlags::PREFER_ES3
;
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
;
347 const bool useEGL
= PR_GetEnv("MOZ_WEBGL_FORCE_EGL");
349 bool tryNativeGL
= true;
350 bool tryANGLE
= false;
356 if (StaticPrefs::webgl_disable_wgl()) {
360 if (StaticPrefs::webgl_disable_angle() ||
361 PR_GetEnv("MOZ_WEBGL_FORCE_OPENGL") || useEGL
) {
367 if (tryNativeGL
&& !forceEnabled
) {
368 FailureReason reason
;
369 if (!gfx::gfxVars::WebglAllowWindowsNativeGl()) {
371 "WebglAllowWindowsNativeGl:false restricts context creation on this "
374 out_failReasons
->push_back(reason
);
376 GenerateWarning("%s", reason
.info
.BeginReading());
383 using fnCreateT
= decltype(gl::GLContextProviderEGL::CreateHeadless
);
384 const auto fnCreate
= [&](fnCreateT
* const pfnCreate
,
385 const char* const info
) {
387 const RefPtr
<gl::GLContext
> gl
= pfnCreate({flags
}, &failureId
);
389 out_failReasons
->push_back(WebGLContext::FailureReason(failureId
, info
));
394 const auto newGL
= [&]() -> RefPtr
<gl::GLContext
> {
397 return fnCreate(&gl::GLContextProviderEGL::CreateHeadless
, "useEGL");
400 fnCreate(&gl::GLContextProvider::CreateHeadless
, "tryNativeGL");
405 return fnCreate(&gl::GLContextProviderEGL::CreateHeadless
, "tryANGLE");
411 out_failReasons
->push_back(
412 FailureReason("FEATURE_FAILURE_WEBGL_EXHAUSTED_DRIVERS",
413 "Exhausted GL driver options."));
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
);
432 const auto val
= StaticPrefs::webgl_debug_incomplete_tex_color();
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());
444 // Fallback for resizes:
446 bool WebGLContext::EnsureDefaultFB() {
448 MOZ_ASSERT(*uvec2::FromSize(mDefaultFB
->mSize
) == mRequestedSize
);
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);
460 if (mOptions
.antialias
) {
461 MOZ_ASSERT(!mDefaultFB
);
462 mDefaultFB
= gl::MozFramebuffer::Create(gl
, attemptSize
, mMsaaSamples
,
464 if (mDefaultFB
) return;
465 if (mOptionsFrozen
) return;
468 MOZ_ASSERT(!mDefaultFB
);
469 mDefaultFB
= gl::MozFramebuffer::Create(gl
, attemptSize
, 0, depthStencil
);
472 if (mDefaultFB
) break;
474 attemptSize
.width
/= 2;
475 attemptSize
.height
/= 2;
479 GenerateWarning("Backbuffer resize failed. Losing context.");
484 mDefaultFB_IsInvalid
= true;
486 const auto actualSize
= *uvec2::FromSize(mDefaultFB
->mSize
);
487 if (actualSize
!= mRequestedSize
) {
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
;
497 void WebGLContext::Resize(uvec2 requestedSize
) {
498 // Zero-sized surfaces can cause problems.
499 if (!requestedSize
.x
) {
502 if (!requestedSize
.y
) {
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
);
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();
533 if (gfxPlatform::InSafeMode()) {
534 failureId
= "FEATURE_FAILURE_WEBGL_SAFEMODE"_ns
;
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
;
545 webgl
= new WebGL2Context(host
, desc
);
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
);
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());
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
;
576 "failIfMajorPerformanceCaveat: Driver is not"
577 " hardware-accelerated.");
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.");
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.");
602 failureId
= "SUCCESS"_ns
;
604 Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_FAILURE_ID
, failureId
);
607 out
->error
= res
.unwrapErr();
610 const auto webgl
= res
.unwrap();
612 // Update our internal stuff:
615 reporter
.SetSuccessful();
616 if (gl::GLContext::ShouldSpew()) {
617 printf_stderr("--- WebGL context created: %p\n", webgl
.get());
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;
639 types
[layers::SurfaceDescriptor::TSurfaceDescriptorMacIOSurface
] = true;
642 types
[layers::SurfaceDescriptor::TSurfaceTextureDescriptor
] = true;
645 types
[layers::SurfaceDescriptor::TSurfaceDescriptorDMABuf
] = true;
652 out
->options
= webgl
->mOptions
;
653 out
->limits
= *webgl
->mLimits
;
654 out
->uploadableSdTypes
= UploadableSdTypes();
655 out
->vendor
= webgl
->gl
->Vendor();
656 out
->optionalRenderableFormatBits
= webgl
->mOptionalRenderableFormatBits
;
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;
680 mOptionsFrozen
= true;
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
);
701 AssertCachedBindings();
702 AssertCachedGlobalState();
704 mShouldPresent
= true;
710 const auto tex
= gl::ScopedTexture(gl
);
711 const auto fb
= gl::ScopedFramebuffer(gl
);
712 gl
->fBindTexture(LOCAL_GL_TEXTURE_2D
, tex
);
713 gl
->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER
, fb
);
714 gl
->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER
, LOCAL_GL_COLOR_ATTACHMENT0
,
715 LOCAL_GL_TEXTURE_2D
, tex
, 0);
717 const auto IsRenderable
= [&](const GLint internalFormat
,
718 const GLenum unpackFormat
) {
719 gl
->fTexImage2D(LOCAL_GL_TEXTURE_2D
, 0, internalFormat
, 1, 1, 0,
720 unpackFormat
, LOCAL_GL_UNSIGNED_BYTE
, nullptr);
721 const auto status
= gl
->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER
);
722 return (status
== LOCAL_GL_FRAMEBUFFER_COMPLETE
);
725 if (IsRenderable(LOCAL_GL_RGB
, LOCAL_GL_RGB
)) {
726 mOptionalRenderableFormatBits
|=
727 webgl::OptionalRenderableFormatBits::RGB8
;
729 if (gl
->IsSupported(gl::GLFeature::sRGB
)) {
733 } formats
= {LOCAL_GL_SRGB8
, LOCAL_GL_RGB
};
734 const bool isEs2
= (gl
->IsGLES() && gl
->Version() < 300);
736 formats
= {LOCAL_GL_SRGB
, LOCAL_GL_SRGB
};
738 if (IsRenderable(formats
.internal
, formats
.unpack
)) {
739 mOptionalRenderableFormatBits
|=
740 webgl::OptionalRenderableFormatBits::SRGB8
;
747 gl
->ResetSyncCallCount("WebGLContext Initialization");
748 LoseLruContextIfLimitExceeded();
751 void WebGLContext::SetCompositableHost(
752 RefPtr
<layers::CompositableHost
>& aCompositableHost
) {
753 mCompositableHost
= aCompositableHost
;
756 void WebGLContext::BumpLruLocked() {
757 if (!mIsContextLost
&& !mPendingContextLoss
) {
758 mLruPosition
.AssignLocked(*this);
760 MOZ_ASSERT(!mLruPosition
.IsInsertedLocked());
764 void WebGLContext::BumpLru() {
765 StaticMutexAutoLock
lock(sLruMutex
);
769 void WebGLContext::LoseLruContextIfLimitExceeded() {
770 StaticMutexAutoLock
lock(sLruMutex
);
772 const auto maxContexts
= std::max(1u, StaticPrefs::webgl_max_contexts());
773 const auto maxContextsPerPrincipal
=
774 std::max(1u, StaticPrefs::webgl_max_contexts_per_principal());
776 // it's important to update the index on a new context before losing old
777 // contexts, otherwise new unused contexts would all have index 0 and we
778 // couldn't distinguish older ones when choosing which one to lose first.
782 size_t forPrincipal
= 0;
783 for (const auto& context
: sLru
) {
784 if (context
->mPrincipalKey
== mPrincipalKey
) {
789 while (forPrincipal
> maxContextsPerPrincipal
) {
790 const auto text
= nsPrintfCString(
791 "Exceeded %u live WebGL contexts for this principal, losing the "
792 "least recently used one.",
793 maxContextsPerPrincipal
);
794 JsWarning(ToString(text
));
796 for (const auto& context
: sLru
) {
797 if (context
->mPrincipalKey
== mPrincipalKey
) {
798 MOZ_ASSERT(context
!= this);
799 context
->LoseContextLruLocked(webgl::ContextLossReason::None
);
807 auto total
= sLru
.size();
808 while (total
> maxContexts
) {
809 const auto text
= nsPrintfCString(
810 "Exceeded %u live WebGL contexts, losing the least "
811 "recently used one.",
813 JsWarning(ToString(text
));
815 const auto& context
= sLru
.front();
816 MOZ_ASSERT(context
!= this);
817 context
->LoseContextLruLocked(webgl::ContextLossReason::None
);
826 ScopedPrepForResourceClear::ScopedPrepForResourceClear(
827 const WebGLContext
& webgl_
)
829 const auto& gl
= webgl
.gl
;
831 if (webgl
.mScissorTestEnabled
) {
832 gl
->fDisable(LOCAL_GL_SCISSOR_TEST
);
834 if (webgl
.mRasterizerDiscardEnabled
) {
835 gl
->fDisable(LOCAL_GL_RASTERIZER_DISCARD
);
838 // "The clear operation always uses the front stencil write mask
839 // when clearing the stencil buffer."
840 webgl
.DoColorMask(Some(0), 0b1111);
841 gl
->fDepthMask(true);
842 gl
->fStencilMaskSeparate(LOCAL_GL_FRONT
, 0xffffffff);
844 gl
->fClearColor(0.0f
, 0.0f
, 0.0f
, 0.0f
);
845 gl
->fClearDepth(1.0f
); // Depth formats are always cleared to 1.0f, not 0.0f.
846 gl
->fClearStencil(0);
849 ScopedPrepForResourceClear::~ScopedPrepForResourceClear() {
850 const auto& gl
= webgl
.gl
;
852 if (webgl
.mScissorTestEnabled
) {
853 gl
->fEnable(LOCAL_GL_SCISSOR_TEST
);
855 if (webgl
.mRasterizerDiscardEnabled
) {
856 gl
->fEnable(LOCAL_GL_RASTERIZER_DISCARD
);
859 webgl
.DoColorMask(Some(0), webgl
.mColorWriteMask0
);
860 gl
->fDepthMask(webgl
.mDepthWriteMask
);
861 gl
->fStencilMaskSeparate(LOCAL_GL_FRONT
, webgl
.mStencilWriteMaskFront
);
863 gl
->fClearColor(webgl
.mColorClearValue
[0], webgl
.mColorClearValue
[1],
864 webgl
.mColorClearValue
[2], webgl
.mColorClearValue
[3]);
865 gl
->fClearDepth(webgl
.mDepthClearValue
);
866 gl
->fClearStencil(webgl
.mStencilClearValue
);
873 void WebGLContext::OnEndOfFrame() {
874 if (StaticPrefs::webgl_perf_spew_frame_allocs()) {
875 GeneratePerfWarning("[webgl.perf.spew-frame-allocs] %" PRIu64
876 " data allocations this frame.",
877 mDataAllocGLCallCount
);
879 mDataAllocGLCallCount
= 0;
880 gl
->ResetSyncCallCount("WebGLContext PresentScreenBuffer");
882 mDrawCallsSinceLastFlush
= 0;
887 void WebGLContext::BlitBackbufferToCurDriverFB(
888 WebGLFramebuffer
* const srcAsWebglFb
,
889 const gl::MozFramebuffer
* const srcAsMozFb
, bool srcIsBGRA
) const {
890 // BlitFramebuffer ignores ColorMask().
892 if (mScissorTestEnabled
) {
893 gl
->fDisable(LOCAL_GL_SCISSOR_TEST
);
897 // If a MozFramebuffer is supplied, ensure that a WebGLFramebuffer is not
898 // used since it might not have completeness info, while the MozFramebuffer
899 // can still supply the needed information.
900 MOZ_ASSERT(!(srcAsMozFb
&& srcAsWebglFb
));
901 const auto* mozFb
= srcAsMozFb
? srcAsMozFb
: mDefaultFB
.get();
905 fbo
= srcAsWebglFb
->mGLName
;
906 const auto* info
= srcAsWebglFb
->GetCompletenessInfo();
908 size
= gfx::IntSize(info
->width
, info
->height
);
914 // If no format conversion is necessary, then attempt to directly blit
915 // between framebuffers. Otherwise, if we need to convert to RGBA from
916 // the source format, then we will need to use the texture blit path
919 if (gl
->IsSupported(gl::GLFeature::framebuffer_blit
)) {
920 gl
->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER
, fbo
);
921 gl
->fBlitFramebuffer(0, 0, size
.width
, size
.height
, 0, 0, size
.width
,
922 size
.height
, LOCAL_GL_COLOR_BUFFER_BIT
,
926 if (mDefaultFB
->mSamples
&&
927 gl
->IsExtensionSupported(
928 gl::GLContext::APPLE_framebuffer_multisample
)) {
929 gl
->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER
, fbo
);
930 gl
->fResolveMultisampleFramebufferAPPLE();
937 const auto& attach
= srcAsWebglFb
->ColorAttachment0();
938 MOZ_ASSERT(attach
.Texture());
939 colorTex
= attach
.Texture()->mGLName
;
941 colorTex
= mozFb
->ColorTex();
944 // DrawBlit handles ColorMask itself.
945 gl
->BlitHelper()->DrawBlitTextureToFramebuffer(
946 colorTex
, size
, size
, LOCAL_GL_TEXTURE_2D
, srcIsBGRA
);
949 if (mScissorTestEnabled
) {
950 gl
->fEnable(LOCAL_GL_SCISSOR_TEST
);
956 template <typename T
, typename
... Args
>
957 constexpr auto MakeArray(Args
... args
) -> std::array
<T
, sizeof...(Args
)> {
958 return {{static_cast<T
>(args
)...}};
961 inline gfx::ColorSpace2
ToColorSpace2(const WebGLContextOptions
& options
) {
962 auto ret
= gfx::ColorSpace2::UNKNOWN
;
964 ret
= gfx::ColorSpace2::SRGB
;
966 if (!options
.ignoreColorSpace
) {
967 ret
= gfx::ToColorSpace2(options
.colorSpace
);
974 // For an overview of how WebGL compositing works, see:
975 // https://wiki.mozilla.org/Platform/GFX/WebGL/Compositing
976 bool WebGLContext::PresentInto(gl::SwapChain
& swapChain
) {
979 if (!ValidateAndInitFB(nullptr)) return false;
982 const auto colorSpace
= ToColorSpace2(mOptions
);
983 auto presenter
= swapChain
.Acquire(mDefaultFB
->mSize
, colorSpace
);
985 GenerateWarning("Swap chain surface creation failed.");
990 const auto destFb
= presenter
->Fb();
991 gl
->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER
, destFb
);
993 BlitBackbufferToCurDriverFB();
995 if (!mOptions
.preserveDrawingBuffer
) {
996 if (gl
->IsSupported(gl::GLFeature::invalidate_framebuffer
)) {
997 gl
->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER
, mDefaultFB
->mFB
);
998 constexpr auto attachments
= MakeArray
<GLenum
>(
999 LOCAL_GL_COLOR_ATTACHMENT0
, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
);
1000 gl
->fInvalidateFramebuffer(LOCAL_GL_READ_FRAMEBUFFER
,
1001 attachments
.size(), attachments
.data());
1003 mDefaultFB_IsInvalid
= true;
1007 if (!mOptions
.alpha
) {
1008 gl
->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER
, destFb
);
1009 gl
->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT
, 4);
1011 gl
->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH
, 0);
1012 gl
->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS
, 0);
1013 gl
->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS
, 0);
1015 uint32_t pixel
= 0xffbadbad;
1016 gl
->fReadPixels(0, 0, 1, 1, LOCAL_GL_RGBA
, LOCAL_GL_UNSIGNED_BYTE
,
1018 MOZ_ASSERT((pixel
& 0xff000000) == 0xff000000);
1026 bool WebGLContext::PresentIntoXR(gl::SwapChain
& swapChain
,
1027 const gl::MozFramebuffer
& fb
) {
1030 const auto colorSpace
= ToColorSpace2(mOptions
);
1031 auto presenter
= swapChain
.Acquire(fb
.mSize
, colorSpace
);
1033 GenerateWarning("Swap chain surface creation failed.");
1038 const auto destFb
= presenter
->Fb();
1039 gl
->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER
, destFb
);
1041 BlitBackbufferToCurDriverFB(nullptr, &fb
);
1043 // https://immersive-web.github.io/webxr/#opaque-framebuffer
1044 // Opaque framebuffers will always be cleared regardless of the
1045 // associated WebGL context’s preserveDrawingBuffer value.
1046 if (gl
->IsSupported(gl::GLFeature::invalidate_framebuffer
)) {
1047 gl
->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER
, fb
.mFB
);
1048 constexpr auto attachments
= MakeArray
<GLenum
>(
1049 LOCAL_GL_COLOR_ATTACHMENT0
, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
);
1050 gl
->fInvalidateFramebuffer(LOCAL_GL_READ_FRAMEBUFFER
, attachments
.size(),
1051 attachments
.data());
1057 // Initialize a swap chain's surface factory given the desired surface type.
1058 void InitSwapChain(gl::GLContext
& gl
, gl::SwapChain
& swapChain
,
1059 const layers::TextureType consumerType
) {
1060 if (!swapChain
.mFactory
) {
1061 auto typedFactory
= gl::SurfaceFactory::Create(&gl
, consumerType
);
1063 swapChain
.mFactory
= std::move(typedFactory
);
1066 if (!swapChain
.mFactory
) {
1067 NS_WARNING("Failed to make an ideal SurfaceFactory.");
1068 swapChain
.mFactory
= MakeUnique
<gl::SurfaceFactory_Basic
>(gl
);
1070 MOZ_ASSERT(swapChain
.mFactory
);
1073 void WebGLContext::Present(WebGLFramebuffer
* const xrFb
,
1074 const layers::TextureType consumerType
,
1076 const webgl::SwapChainOptions
& options
) {
1077 const FuncScope
funcScope(*this, "<Present>");
1078 if (IsContextLost()) {
1079 EnsureContextLostRemoteTextureOwner(options
);
1083 auto swapChain
= GetSwapChain(xrFb
, webvr
);
1084 const gl::MozFramebuffer
* maybeFB
= nullptr;
1086 maybeFB
= xrFb
->mOpaque
.get();
1088 mResolvedDefaultFB
= nullptr;
1091 InitSwapChain(*gl
, *swapChain
, consumerType
);
1094 maybeFB
? PresentIntoXR(*swapChain
, *maybeFB
) : PresentInto(*swapChain
);
1096 EnsureContextLostRemoteTextureOwner(options
);
1100 bool useAsync
= options
.remoteTextureOwnerId
.IsValid() &&
1101 options
.remoteTextureId
.IsValid();
1103 PushRemoteTexture(nullptr, *swapChain
, swapChain
->FrontBuffer(), options
);
1107 void WebGLContext::WaitForTxn(layers::RemoteTextureOwnerId ownerId
,
1108 layers::RemoteTextureTxnType txnType
,
1109 layers::RemoteTextureTxnId txnId
) {
1110 if (!ownerId
.IsValid() || !txnType
|| !txnId
) {
1113 if (mRemoteTextureOwner
&& mRemoteTextureOwner
->IsRegistered(ownerId
)) {
1114 mRemoteTextureOwner
->WaitForTxn(ownerId
, txnType
, txnId
);
1118 bool WebGLContext::CopyToSwapChain(
1119 WebGLFramebuffer
* const srcFb
, const layers::TextureType consumerType
,
1120 const webgl::SwapChainOptions
& options
,
1121 layers::RemoteTextureOwnerClient
* ownerClient
) {
1122 const FuncScope
funcScope(*this, "<CopyToSwapChain>");
1123 if (IsContextLost()) {
1132 const auto* info
= srcFb
->GetCompletenessInfo();
1136 gfx::IntSize
size(info
->width
, info
->height
);
1138 InitSwapChain(*gl
, srcFb
->mSwapChain
, consumerType
);
1140 bool useAsync
= options
.remoteTextureOwnerId
.IsValid() &&
1141 options
.remoteTextureId
.IsValid();
1142 // If we're using async present and if there is no way to serialize surfaces,
1143 // then a readback is required to do the copy. In this case, there's no reason
1144 // to copy into a separate shared surface for the front buffer. Just directly
1145 // read back the WebGL framebuffer into and push it as a remote texture.
1146 if (useAsync
&& srcFb
->mSwapChain
.mFactory
->GetConsumerType() ==
1147 layers::TextureType::Unknown
) {
1148 return PushRemoteTexture(srcFb
, srcFb
->mSwapChain
, nullptr, options
,
1153 // ColorSpace will need to be part of SwapChainOptions for DTWebgl.
1154 const auto colorSpace
= ToColorSpace2(mOptions
);
1155 auto presenter
= srcFb
->mSwapChain
.Acquire(size
, colorSpace
);
1157 GenerateWarning("Swap chain surface creation failed.");
1162 const ScopedFBRebinder
saveFB(this);
1164 const auto destFb
= presenter
->Fb();
1165 gl
->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER
, destFb
);
1167 BlitBackbufferToCurDriverFB(srcFb
, nullptr, options
.bgra
);
1171 return PushRemoteTexture(srcFb
, srcFb
->mSwapChain
,
1172 srcFb
->mSwapChain
.FrontBuffer(), options
,
1178 bool WebGLContext::PushRemoteTexture(
1179 WebGLFramebuffer
* fb
, gl::SwapChain
& swapChain
,
1180 std::shared_ptr
<gl::SharedSurface
> surf
,
1181 const webgl::SwapChainOptions
& options
,
1182 layers::RemoteTextureOwnerClient
* ownerClient
) {
1183 const auto onFailure
= [&]() -> bool {
1184 GenerateWarning("Remote texture creation failed.");
1186 if (ownerClient
&& ownerClient
== mRemoteTextureOwner
) {
1187 ownerClient
->PushDummyTexture(options
.remoteTextureId
,
1188 options
.remoteTextureOwnerId
);
1194 if (!mRemoteTextureOwner
) {
1195 // Ensure we have a remote texture owner client for WebGLParent.
1196 const auto* outOfProcess
=
1197 mHost
? mHost
->mOwnerData
.outOfProcess
: nullptr;
1198 if (!outOfProcess
) {
1201 auto pid
= outOfProcess
->OtherPid();
1202 mRemoteTextureOwner
= MakeRefPtr
<layers::RemoteTextureOwnerClient
>(pid
);
1204 ownerClient
= mRemoteTextureOwner
;
1207 layers::RemoteTextureOwnerId ownerId
= options
.remoteTextureOwnerId
;
1208 layers::RemoteTextureId textureId
= options
.remoteTextureId
;
1210 if (!ownerClient
->IsRegistered(ownerId
)) {
1211 // Register a texture owner to represent the swap chain.
1212 RefPtr
<layers::RemoteTextureOwnerClient
> textureOwner
= ownerClient
;
1213 auto destroyedCallback
= [textureOwner
, ownerId
]() {
1214 textureOwner
->UnregisterTextureOwner(ownerId
);
1217 swapChain
.SetDestroyedCallback(destroyedCallback
);
1218 ownerClient
->RegisterTextureOwner(ownerId
,
1219 /* aSharedRecycling */ !!fb
);
1222 MOZ_ASSERT(fb
|| surf
);
1225 size
= surf
->mDesc
.size
;
1227 const auto* info
= fb
->GetCompletenessInfo();
1229 size
= gfx::IntSize(info
->width
, info
->height
);
1232 const auto surfaceFormat
= mOptions
.alpha
? gfx::SurfaceFormat::B8G8R8A8
1233 : gfx::SurfaceFormat::B8G8R8X8
;
1234 Maybe
<layers::SurfaceDescriptor
> desc
;
1236 desc
= surf
->ToSurfaceDescriptor();
1239 if (surf
&& surf
->mDesc
.type
!= gl::SharedSurfaceType::Basic
) {
1242 // If we can't serialize to a surface descriptor, then we need to create
1243 // a buffer to read back into that will become the remote texture.
1244 auto data
= ownerClient
->CreateOrRecycleBufferTextureData(
1245 size
, surfaceFormat
, ownerId
);
1247 gfxCriticalNoteOnce
<< "Failed to allocate BufferTextureData";
1251 layers::MappedTextureData mappedData
;
1252 if (!data
->BorrowMappedData(mappedData
)) {
1256 Range
<uint8_t> range
= {mappedData
.data
,
1257 data
->AsBufferTextureData()->GetBufferSize()};
1259 // If we have a surface representing the front buffer, then try to snapshot
1260 // that. Otherwise, when there is no surface, we read back directly from the
1261 // WebGL framebuffer.
1263 surf
? FrontBufferSnapshotInto(surf
, Some(range
),
1264 Some(mappedData
.stride
))
1265 : SnapshotInto(fb
->mGLName
, size
, range
, Some(mappedData
.stride
));
1270 if (!options
.bgra
) {
1271 // If the buffer is already BGRA, we don't need to swizzle. However, if it
1272 // is RGBA, then a swizzle to BGRA is required.
1273 bool rv
= gfx::SwizzleData(mappedData
.data
, mappedData
.stride
,
1274 gfx::SurfaceFormat::R8G8B8A8
, mappedData
.data
,
1276 gfx::SurfaceFormat::B8G8R8A8
, mappedData
.size
);
1277 MOZ_RELEASE_ASSERT(rv
, "SwizzleData failed!");
1280 ownerClient
->PushTexture(textureId
, ownerId
, std::move(data
));
1284 // SharedSurfaces of SurfaceDescriptorD3D10 and SurfaceDescriptorMacIOSurface
1285 // need to be kept alive. They will be recycled by
1286 // RemoteTextureOwnerClient::GetRecycledSharedSurface() when their usages are
1288 std::shared_ptr
<gl::SharedSurface
> keepAlive
;
1289 switch (desc
->type()) {
1290 case layers::SurfaceDescriptor::TSurfaceDescriptorD3D10
:
1291 case layers::SurfaceDescriptor::TSurfaceDescriptorMacIOSurface
:
1292 case layers::SurfaceDescriptor::TSurfaceTextureDescriptor
:
1293 case layers::SurfaceDescriptor::TSurfaceDescriptorAndroidHardwareBuffer
:
1294 case layers::SurfaceDescriptor::TSurfaceDescriptorDMABuf
:
1301 ownerClient
->PushTexture(textureId
, ownerId
, keepAlive
, size
, surfaceFormat
,
1304 // Look for a recycled surface that matches the swap chain.
1305 while (auto recycledSurface
= ownerClient
->GetRecycledSharedSurface(
1306 size
, surfaceFormat
, desc
->type(), ownerId
)) {
1307 if (swapChain
.StoreRecycledSurface(recycledSurface
)) {
1314 void WebGLContext::EnsureContextLostRemoteTextureOwner(
1315 const webgl::SwapChainOptions
& options
) {
1316 if (!options
.remoteTextureOwnerId
.IsValid()) {
1320 if (!mRemoteTextureOwner
) {
1321 // Ensure we have a remote texture owner client for WebGLParent.
1322 const auto* outOfProcess
= mHost
? mHost
->mOwnerData
.outOfProcess
: nullptr;
1323 if (!outOfProcess
) {
1326 auto pid
= outOfProcess
->OtherPid();
1327 mRemoteTextureOwner
= MakeRefPtr
<layers::RemoteTextureOwnerClient
>(pid
);
1330 layers::RemoteTextureOwnerId ownerId
= options
.remoteTextureOwnerId
;
1332 if (!mRemoteTextureOwner
->IsRegistered(ownerId
)) {
1333 mRemoteTextureOwner
->RegisterTextureOwner(ownerId
);
1335 mRemoteTextureOwner
->NotifyContextLost();
1338 void WebGLContext::EndOfFrame() {
1339 const FuncScope
funcScope(*this, "<EndOfFrame>");
1340 if (IsContextLost()) return;
1345 gl::SwapChain
* WebGLContext::GetSwapChain(WebGLFramebuffer
* const xrFb
,
1347 auto swapChain
= webvr
? &mWebVRSwapChain
: &mSwapChain
;
1349 swapChain
= &xrFb
->mSwapChain
;
1354 Maybe
<layers::SurfaceDescriptor
> WebGLContext::GetFrontBuffer(
1355 WebGLFramebuffer
* const xrFb
, const bool webvr
) {
1356 auto* swapChain
= GetSwapChain(xrFb
, webvr
);
1357 if (!swapChain
) return {};
1358 const auto& front
= swapChain
->FrontBuffer();
1359 if (!front
) return {};
1361 return front
->ToSurfaceDescriptor();
1364 Maybe
<uvec2
> WebGLContext::FrontBufferSnapshotInto(
1365 const Maybe
<Range
<uint8_t>> maybeDest
, const Maybe
<size_t> destStride
) {
1366 const auto& front
= mSwapChain
.FrontBuffer();
1367 if (!front
) return {};
1368 return FrontBufferSnapshotInto(front
, maybeDest
, destStride
);
1371 Maybe
<uvec2
> WebGLContext::FrontBufferSnapshotInto(
1372 const std::shared_ptr
<gl::SharedSurface
>& front
,
1373 const Maybe
<Range
<uint8_t>> maybeDest
, const Maybe
<size_t> destStride
) {
1374 const auto& size
= front
->mDesc
.size
;
1375 if (!maybeDest
) return Some(*uvec2::FromSize(size
));
1379 front
->WaitForBufferOwnership();
1381 front
->ProducerReadAcquire();
1382 auto reset
= MakeScopeExit([&] {
1383 front
->ProducerReadRelease();
1384 front
->UnlockProd();
1389 return SnapshotInto(front
->mFb
? front
->mFb
->mFB
: 0, size
, *maybeDest
,
1393 Maybe
<uvec2
> WebGLContext::SnapshotInto(GLuint srcFb
, const gfx::IntSize
& size
,
1394 const Range
<uint8_t>& dest
,
1395 const Maybe
<size_t> destStride
) {
1396 const auto minStride
= CheckedInt
<size_t>(size
.width
) * 4;
1397 if (!minStride
.isValid()) {
1398 gfxCriticalError() << "SnapshotInto: invalid stride, width:" << size
.width
;
1401 size_t stride
= destStride
.valueOr(minStride
.value());
1402 if (stride
< minStride
.value() || (stride
% 4) != 0) {
1403 gfxCriticalError() << "SnapshotInto: invalid stride, width:" << size
.width
1404 << ", stride:" << stride
;
1408 gl
->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT
, 1);
1410 gl
->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH
,
1411 stride
> minStride
.value() ? stride
/ 4 : 0);
1412 gl
->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS
, 0);
1413 gl
->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS
, 0);
1418 const auto readFbWas
= mBoundReadFramebuffer
;
1419 const auto pboWas
= mBoundPixelPackBuffer
;
1421 GLenum fbTarget
= LOCAL_GL_READ_FRAMEBUFFER
;
1423 fbTarget
= LOCAL_GL_FRAMEBUFFER
;
1425 auto reset2
= MakeScopeExit([&] {
1426 DoBindFB(readFbWas
, fbTarget
);
1428 BindBuffer(LOCAL_GL_PIXEL_PACK_BUFFER
, pboWas
);
1432 gl
->fBindFramebuffer(fbTarget
, srcFb
);
1434 BindBuffer(LOCAL_GL_PIXEL_PACK_BUFFER
, nullptr);
1439 const auto srcByteCount
= CheckedInt
<size_t>(stride
) * size
.height
;
1440 if (!srcByteCount
.isValid()) {
1441 gfxCriticalError() << "SnapshotInto: invalid srcByteCount, width:"
1442 << size
.width
<< ", height:" << size
.height
;
1445 const auto dstByteCount
= dest
.length();
1446 if (srcByteCount
.value() > dstByteCount
) {
1447 gfxCriticalError() << "SnapshotInto: srcByteCount:" << srcByteCount
.value()
1448 << " > dstByteCount:" << dstByteCount
;
1451 uint8_t* dstPtr
= dest
.begin().get();
1452 gl
->fReadPixels(0, 0, size
.width
, size
.height
, LOCAL_GL_RGBA
,
1453 LOCAL_GL_UNSIGNED_BYTE
, dstPtr
);
1455 if (!IsWebGL2() && stride
> minStride
.value() && size
.height
> 1) {
1456 // WebGL 1 doesn't support PACK_ROW_LENGTH. Instead, we read the data tight
1457 // into the front of the buffer, and use memmove (since the source and dest
1458 // may overlap) starting from the back to move it to the correct stride
1459 // offsets. We don't move the first row as it is already in the right place.
1460 uint8_t* destRow
= dstPtr
+ stride
* (size
.height
- 1);
1461 const uint8_t* srcRow
= dstPtr
+ minStride
.value() * (size
.height
- 1);
1462 while (destRow
> dstPtr
) {
1463 memmove(destRow
, srcRow
, minStride
.value());
1465 srcRow
-= minStride
.value();
1469 return Some(*uvec2::FromSize(size
));
1472 void WebGLContext::ClearVRSwapChain() { mWebVRSwapChain
.ClearPool(); }
1474 // ------------------------
1476 RefPtr
<gfx::DataSourceSurface
> GetTempSurface(const gfx::IntSize
& aSize
,
1477 gfx::SurfaceFormat
& aFormat
) {
1479 gfx::GetAlignedStride
<8>(aSize
.width
, BytesPerPixel(aFormat
));
1480 return gfx::Factory::CreateDataSourceSurfaceWithStride(aSize
, aFormat
,
1484 void WebGLContext::DummyReadFramebufferOperation() {
1485 if (!mBoundReadFramebuffer
) return; // Infallible.
1487 const auto status
= mBoundReadFramebuffer
->CheckFramebufferStatus();
1488 if (status
!= LOCAL_GL_FRAMEBUFFER_COMPLETE
) {
1489 ErrorInvalidFramebufferOperation("Framebuffer must be complete.");
1493 dom::ContentParentId
WebGLContext::GetContentId() const {
1494 const auto* outOfProcess
= mHost
? mHost
->mOwnerData
.outOfProcess
: nullptr;
1496 return outOfProcess
->mContentId
;
1498 if (XRE_IsContentProcess()) {
1499 return dom::ContentChild::GetSingleton()->GetID();
1501 return dom::ContentParentId();
1504 bool WebGLContext::Has64BitTimestamps() const {
1505 // 'sync' provides glGetInteger64v either by supporting ARB_sync, GL3+, or
1507 return gl
->IsSupported(gl::GLFeature::sync
);
1510 static bool CheckContextLost(gl::GLContext
* gl
, bool* const out_isGuilty
) {
1513 const auto resetStatus
= gl
->fGetGraphicsResetStatus();
1514 if (resetStatus
== LOCAL_GL_NO_ERROR
) {
1515 *out_isGuilty
= false;
1519 // Assume guilty unless we find otherwise!
1520 bool isGuilty
= true;
1521 switch (resetStatus
) {
1522 case LOCAL_GL_INNOCENT_CONTEXT_RESET_ARB
:
1523 case LOCAL_GL_PURGED_CONTEXT_RESET_NV
:
1524 // Either nothing wrong, or not our fault.
1527 case LOCAL_GL_GUILTY_CONTEXT_RESET_ARB
:
1529 "WebGL content on the page definitely caused the graphics"
1532 case LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB
:
1534 "WebGL content on the page might have caused the graphics"
1536 // If we can't tell, assume not-guilty.
1537 // Todo: Implement max number of "unknown" resets per document or time.
1541 gfxCriticalError() << "Unexpected glGetGraphicsResetStatus: "
1542 << gfx::hexa(resetStatus
);
1548 "WebGL context on this page is considered guilty, and will"
1549 " not be restored.");
1552 *out_isGuilty
= isGuilty
;
1556 void WebGLContext::RunContextLossTimer() { mContextLossHandler
.RunTimer(); }
1558 // We use this timer for many things. Here are the things that it is activated
1560 // 1) If a script is using the MOZ_WEBGL_lose_context extension.
1561 // 2) If we are using EGL and _NOT ANGLE_, we query periodically to see if the
1562 // CONTEXT_LOST_WEBGL error has been triggered.
1563 // 3) If we are using ANGLE, or anything that supports ARB_robustness, query the
1564 // GPU periodically to see if the reset status bit has been set.
1565 // In all of these situations, we use this timer to send the script context lost
1566 // and restored events asynchronously. For example, if it triggers a context
1567 // loss, the webglcontextlost event will be sent to it the next time the
1568 // robustness timer fires.
1569 // Note that this timer mechanism is not used unless one of these 3 criteria are
1571 // At a bare minimum, from context lost to context restores, it would take 3
1572 // full timer iterations: detection, webglcontextlost, webglcontextrestored.
1573 void WebGLContext::CheckForContextLoss() {
1574 bool isGuilty
= true;
1575 const auto isContextLost
= CheckContextLost(gl
, &isGuilty
);
1576 if (!isContextLost
) return;
1578 mWebGLError
= LOCAL_GL_CONTEXT_LOST
;
1580 auto reason
= webgl::ContextLossReason::None
;
1582 reason
= webgl::ContextLossReason::Guilty
;
1584 LoseContext(reason
);
1587 void WebGLContext::HandlePendingContextLoss() {
1588 mIsContextLost
= true;
1590 mHost
->OnContextLoss(mPendingContextLossReason
);
1594 void WebGLContext::LoseContextLruLocked(const webgl::ContextLossReason reason
) {
1595 printf_stderr("WebGL(%p)::LoseContext(%u)\n", this,
1596 static_cast<uint32_t>(reason
));
1597 mLruPosition
.ResetLocked();
1598 mPendingContextLossReason
= reason
;
1599 mPendingContextLoss
= true;
1602 void WebGLContext::LoseContext(const webgl::ContextLossReason reason
) {
1603 StaticMutexAutoLock
lock(sLruMutex
);
1604 LoseContextLruLocked(reason
);
1605 HandlePendingContextLoss();
1606 if (mRemoteTextureOwner
) {
1607 mRemoteTextureOwner
->NotifyContextLost();
1611 void WebGLContext::DidRefresh() {
1613 gl
->FlushIfHeavyGLCallsSinceLastFlush();
1617 ////////////////////////////////////////////////////////////////////////////////
1619 uvec2
WebGLContext::DrawingBufferSize() {
1620 const FuncScope
funcScope(*this, "width/height");
1621 if (IsContextLost()) return {};
1623 if (!EnsureDefaultFB()) return {};
1625 return *uvec2::FromSize(mDefaultFB
->mSize
);
1628 bool WebGLContext::ValidateAndInitFB(const WebGLFramebuffer
* const fb
,
1629 const GLenum incompleteFbError
) {
1630 if (fb
) return fb
->ValidateAndInitAttachments(incompleteFbError
);
1632 if (!EnsureDefaultFB()) return false;
1634 if (mDefaultFB_IsInvalid
) {
1636 gl
->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER
, mDefaultFB
->mFB
);
1637 const webgl::ScopedPrepForResourceClear
scopedPrep(*this);
1638 if (!mOptions
.alpha
) {
1639 gl
->fClearColor(0, 0, 0, 1);
1641 const GLbitfield bits
= LOCAL_GL_COLOR_BUFFER_BIT
|
1642 LOCAL_GL_DEPTH_BUFFER_BIT
|
1643 LOCAL_GL_STENCIL_BUFFER_BIT
;
1646 mDefaultFB_IsInvalid
= false;
1651 void WebGLContext::DoBindFB(const WebGLFramebuffer
* const fb
,
1652 const GLenum target
) const {
1653 const GLenum driverFB
= fb
? fb
->mGLName
: mDefaultFB
->mFB
;
1654 gl
->fBindFramebuffer(target
, driverFB
);
1657 bool WebGLContext::BindCurFBForDraw() {
1658 const auto& fb
= mBoundDrawFramebuffer
;
1659 if (!ValidateAndInitFB(fb
)) return false;
1665 bool WebGLContext::BindCurFBForColorRead(
1666 const webgl::FormatUsageInfo
** const out_format
, uint32_t* const out_width
,
1667 uint32_t* const out_height
, const GLenum incompleteFbError
) {
1668 const auto& fb
= mBoundReadFramebuffer
;
1671 if (!ValidateAndInitFB(fb
, incompleteFbError
)) return false;
1672 if (!fb
->ValidateForColorRead(out_format
, out_width
, out_height
))
1675 gl
->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER
, fb
->mGLName
);
1679 if (!BindDefaultFBForRead()) return false;
1681 if (mDefaultFB_ReadBuffer
== LOCAL_GL_NONE
) {
1682 ErrorInvalidOperation(
1683 "Can't read from backbuffer when readBuffer mode is NONE.");
1687 auto effFormat
= mOptions
.alpha
? webgl::EffectiveFormat::RGBA8
1688 : webgl::EffectiveFormat::RGB8
;
1690 *out_format
= mFormatUsage
->GetUsage(effFormat
);
1691 MOZ_ASSERT(*out_format
);
1693 *out_width
= mDefaultFB
->mSize
.width
;
1694 *out_height
= mDefaultFB
->mSize
.height
;
1698 bool WebGLContext::BindDefaultFBForRead() {
1699 if (!ValidateAndInitFB(nullptr)) return false;
1701 if (!mDefaultFB
->mSamples
) {
1702 gl
->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER
, mDefaultFB
->mFB
);
1706 if (!mResolvedDefaultFB
) {
1707 mResolvedDefaultFB
=
1708 gl::MozFramebuffer::Create(gl
, mDefaultFB
->mSize
, 0, false);
1709 if (!mResolvedDefaultFB
) {
1710 gfxCriticalNote
<< FuncName() << ": Failed to create mResolvedDefaultFB.";
1715 gl
->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER
, mResolvedDefaultFB
->mFB
);
1716 BlitBackbufferToCurDriverFB();
1718 gl
->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER
, mResolvedDefaultFB
->mFB
);
1722 void WebGLContext::DoColorMask(Maybe
<GLuint
> i
, const uint8_t bitmask
) const {
1723 if (!IsExtensionEnabled(WebGLExtensionID::OES_draw_buffers_indexed
)) {
1726 const auto bs
= std::bitset
<4>(bitmask
);
1728 gl
->fColorMaski(*i
, bs
[0], bs
[1], bs
[2], bs
[3]);
1730 gl
->fColorMask(bs
[0], bs
[1], bs
[2], bs
[3]);
1734 ////////////////////////////////////////////////////////////////////////////////
1736 ScopedDrawCallWrapper::ScopedDrawCallWrapper(WebGLContext
& webgl
)
1738 uint8_t driverColorMask0
= mWebGL
.mColorWriteMask0
;
1739 bool driverDepthTest
= mWebGL
.mDepthTestEnabled
;
1740 bool driverStencilTest
= mWebGL
.mStencilTestEnabled
;
1741 const auto& fb
= mWebGL
.mBoundDrawFramebuffer
;
1743 if (mWebGL
.mDefaultFB_DrawBuffer0
== LOCAL_GL_NONE
) {
1744 driverColorMask0
= 0; // Is this well-optimized enough for depth-first
1747 driverColorMask0
&= ~(uint8_t(mWebGL
.mNeedsFakeNoAlpha
) << 3);
1749 driverDepthTest
&= !mWebGL
.mNeedsFakeNoDepth
;
1750 driverStencilTest
&= !mWebGL
.mNeedsFakeNoStencil
;
1753 const auto& gl
= mWebGL
.gl
;
1754 mWebGL
.DoColorMask(Some(0), driverColorMask0
);
1755 if (mWebGL
.mDriverDepthTest
!= driverDepthTest
) {
1756 // "When disabled, the depth comparison and subsequent possible updates to
1758 // depth buffer value are bypassed and the fragment is passed to the next
1759 // operation." [GLES 3.0.5, p177]
1760 mWebGL
.mDriverDepthTest
= driverDepthTest
;
1761 gl
->SetEnabled(LOCAL_GL_DEPTH_TEST
, mWebGL
.mDriverDepthTest
);
1763 if (mWebGL
.mDriverStencilTest
!= driverStencilTest
) {
1764 // "When disabled, the stencil test and associated modifications are not
1766 // the fragment is always passed." [GLES 3.0.5, p175]
1767 mWebGL
.mDriverStencilTest
= driverStencilTest
;
1768 gl
->SetEnabled(LOCAL_GL_STENCIL_TEST
, mWebGL
.mDriverStencilTest
);
1772 ScopedDrawCallWrapper::~ScopedDrawCallWrapper() {
1773 if (mWebGL
.mBoundDrawFramebuffer
) return;
1775 mWebGL
.mResolvedDefaultFB
= nullptr;
1776 mWebGL
.mShouldPresent
= true;
1781 void WebGLContext::ScissorRect::Apply(gl::GLContext
& gl
) const {
1782 gl
.fScissor(x
, y
, w
, h
);
1785 ////////////////////////////////////////
1787 IndexedBufferBinding::IndexedBufferBinding() = default;
1788 IndexedBufferBinding::~IndexedBufferBinding() = default;
1790 uint64_t IndexedBufferBinding::ByteCount() const {
1791 if (!mBufferBinding
) return 0;
1793 uint64_t bufferSize
= mBufferBinding
->ByteLength();
1794 if (!mRangeSize
) // BindBufferBase
1797 if (mRangeStart
>= bufferSize
) return 0;
1798 bufferSize
-= mRangeStart
;
1800 return std::min(bufferSize
, mRangeSize
);
1803 ////////////////////////////////////////
1805 ScopedFBRebinder::~ScopedFBRebinder() {
1806 const auto fnName
= [&](WebGLFramebuffer
* fb
) {
1807 return fb
? fb
->mGLName
: 0;
1810 const auto& gl
= mWebGL
->gl
;
1811 if (mWebGL
->IsWebGL2()) {
1812 gl
->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
,
1813 fnName(mWebGL
->mBoundDrawFramebuffer
));
1814 gl
->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER
,
1815 fnName(mWebGL
->mBoundReadFramebuffer
));
1817 MOZ_ASSERT(mWebGL
->mBoundDrawFramebuffer
== mWebGL
->mBoundReadFramebuffer
);
1818 gl
->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER
,
1819 fnName(mWebGL
->mBoundDrawFramebuffer
));
1823 ////////////////////
1825 void DoBindBuffer(gl::GLContext
& gl
, const GLenum target
,
1826 const WebGLBuffer
* const buffer
) {
1827 gl
.fBindBuffer(target
, buffer
? buffer
->mGLName
: 0);
1830 ////////////////////////////////////////
1832 bool Intersect(const int32_t srcSize
, const int32_t read0
,
1833 const int32_t readSize
, int32_t* const out_intRead0
,
1834 int32_t* const out_intWrite0
, int32_t* const out_intSize
) {
1835 MOZ_ASSERT(srcSize
>= 0);
1836 MOZ_ASSERT(readSize
>= 0);
1837 const auto read1
= int64_t(read0
) + readSize
;
1839 int32_t intRead0
= read0
; // Clearly doesn't need validation.
1840 int64_t intWrite0
= 0;
1841 int64_t intSize
= readSize
;
1843 if (read1
<= 0 || read0
>= srcSize
) {
1848 const auto diff
= int64_t(0) - read0
;
1849 MOZ_ASSERT(diff
>= 0);
1854 if (read1
> srcSize
) {
1855 const auto diff
= int64_t(read1
) - srcSize
;
1856 MOZ_ASSERT(diff
>= 0);
1860 if (!CheckedInt
<int32_t>(intWrite0
).isValid() ||
1861 !CheckedInt
<int32_t>(intSize
).isValid()) {
1866 *out_intRead0
= intRead0
;
1867 *out_intWrite0
= intWrite0
;
1868 *out_intSize
= intSize
;
1874 uint64_t AvailGroups(const uint64_t totalAvailItems
,
1875 const uint64_t firstItemOffset
, const uint32_t groupSize
,
1876 const uint32_t groupStride
) {
1877 MOZ_ASSERT(groupSize
&& groupStride
);
1878 MOZ_ASSERT(groupSize
<= groupStride
);
1880 if (totalAvailItems
<= firstItemOffset
) return 0;
1881 const size_t availItems
= totalAvailItems
- firstItemOffset
;
1883 size_t availGroups
= availItems
/ groupStride
;
1884 const size_t tailItems
= availItems
% groupStride
;
1885 if (tailItems
>= groupSize
) {
1891 ////////////////////////////////////////////////////////////////////////////////
1893 const char* WebGLContext::FuncName() const {
1895 if (MOZ_LIKELY(mFuncScope
)) {
1896 ret
= mFuncScope
->mFuncName
;
1898 NS_WARNING("FuncScope not on stack!");
1899 ret
= "<unknown function>";
1906 WebGLContext::FuncScope::FuncScope(const WebGLContext
& webgl
,
1907 const char* const funcName
)
1908 : mWebGL(webgl
), mFuncName(bool(mWebGL
.mFuncScope
) ? nullptr : funcName
) {
1909 if (!mFuncName
) return;
1910 mWebGL
.mFuncScope
= this;
1913 WebGLContext::FuncScope::~FuncScope() {
1914 if (mBindFailureGuard
) {
1915 gfxCriticalError() << "mBindFailureGuard failure: Early exit from "
1916 << mWebGL
.FuncName();
1919 if (!mFuncName
) return;
1920 mWebGL
.mFuncScope
= nullptr;
1925 bool ClientWebGLContext::IsXRCompatible() const { return mXRCompatible
; }
1927 already_AddRefed
<dom::Promise
> ClientWebGLContext::MakeXRCompatible(
1929 const FuncScope
funcScope(*this, "MakeXRCompatible");
1930 nsCOMPtr
<nsIGlobalObject
> global
= GetParentObject();
1932 aRv
.ThrowInvalidAccessError(
1933 "Using a WebGL context that is not attached to either a canvas or an "
1937 RefPtr
<dom::Promise
> promise
= dom::Promise::Create(global
, aRv
);
1938 NS_ENSURE_TRUE(!aRv
.Failed(), nullptr);
1940 if (IsContextLost()) {
1941 promise
->MaybeRejectWithInvalidStateError(
1942 "Can not make context XR compatible when context is already lost.");
1943 return promise
.forget();
1946 // TODO: Bug 1580258 - WebGLContext.MakeXRCompatible needs to switch to
1947 // the device connected to the XR hardware
1948 // This should update `options` and lose+restore the context.
1949 mXRCompatible
= true;
1950 promise
->MaybeResolveWithUndefined();
1951 return promise
.forget();
1956 webgl::AvailabilityRunnable
& ClientWebGLContext::EnsureAvailabilityRunnable()
1958 if (!mAvailabilityRunnable
) {
1959 mAvailabilityRunnable
= new webgl::AvailabilityRunnable(this);
1960 auto forgettable
= mAvailabilityRunnable
;
1961 NS_DispatchToCurrentThread(forgettable
.forget());
1963 return *mAvailabilityRunnable
;
1966 webgl::AvailabilityRunnable::AvailabilityRunnable(
1967 const ClientWebGLContext
* const webgl
)
1968 : DiscardableRunnable("webgl::AvailabilityRunnable"), mWebGL(webgl
) {}
1970 webgl::AvailabilityRunnable::~AvailabilityRunnable() {
1971 MOZ_ASSERT(mQueries
.empty());
1972 MOZ_ASSERT(mSyncs
.empty());
1975 nsresult
webgl::AvailabilityRunnable::Run() {
1976 for (const auto& cur
: mQueries
) {
1978 cur
->mCanBeAvailable
= true;
1982 for (const auto& cur
: mSyncs
) {
1984 cur
->mCanBeAvailable
= true;
1989 mWebGL
->mAvailabilityRunnable
= nullptr;
1996 void WebGLContext::JsWarning(const std::string
& text
) const {
1998 mHost
->JsWarning(text
);
2002 NS_WARNING(text
.c_str());
2007 void WebGLContext::GenerateErrorImpl(const GLenum errOrWarning
,
2008 const std::string
& text
) const {
2009 auto err
= errOrWarning
;
2010 bool isPerfWarning
= false;
2011 if (err
== webgl::kErrorPerfWarning
) {
2013 isPerfWarning
= true;
2016 if (err
&& mFuncScope
&& mFuncScope
->mBindFailureGuard
) {
2017 gfxCriticalError() << "mBindFailureGuard failure: Generating error "
2018 << EnumString(err
) << ": " << text
;
2021 /* ES2 section 2.5 "GL Errors" states that implementations can have
2022 * multiple 'flags', as errors might be caught in different parts of
2023 * a distributed implementation.
2024 * We're signing up as a distributed implementation here, with
2025 * separate flags for WebGL and the underlying GLContext.
2027 if (!mWebGLError
) mWebGLError
= err
;
2029 if (!mHost
) return; // Impossible?
2033 const auto ShouldWarn
= [&]() {
2034 if (isPerfWarning
) {
2035 return ShouldGeneratePerfWarnings();
2037 return ShouldGenerateWarnings();
2039 if (!ShouldWarn()) return;
2043 auto* pNumWarnings
= &mWarningCount
;
2044 const char* warningsType
= "warnings";
2045 if (isPerfWarning
) {
2046 pNumWarnings
= &mNumPerfWarnings
;
2047 warningsType
= "perf warnings";
2050 if (isPerfWarning
) {
2051 const auto perfText
= std::string("WebGL perf warning: ") + text
;
2052 JsWarning(perfText
);
2058 if (!ShouldWarn()) {
2059 const auto& msg
= nsPrintfCString(
2060 "After reporting %i, no further %s will be reported for this WebGL "
2062 int(*pNumWarnings
), warningsType
);
2063 JsWarning(ToString(msg
));
2069 Maybe
<std::string
> WebGLContext::GetString(const GLenum pname
) const {
2070 const WebGLContext::FuncScope
funcScope(*this, "getParameter");
2071 if (IsContextLost()) return {};
2073 const auto FromRaw
= [](const char* const raw
) -> Maybe
<std::string
> {
2074 if (!raw
) return {};
2075 return Some(std::string(raw
));
2079 case LOCAL_GL_EXTENSIONS
: {
2080 if (!gl
->IsCoreProfile()) {
2081 const auto rawExt
= (const char*)gl
->fGetString(LOCAL_GL_EXTENSIONS
);
2082 return FromRaw(rawExt
);
2085 const auto& numExts
= gl
->GetIntAs
<GLuint
>(LOCAL_GL_NUM_EXTENSIONS
);
2086 for (GLuint i
= 0; i
< numExts
; i
++) {
2088 (const char*)gl
->fGetStringi(LOCAL_GL_EXTENSIONS
, i
);
2089 if (!rawExt
) continue;
2096 return Some(std::move(ret
));
2099 case LOCAL_GL_RENDERER
:
2100 case LOCAL_GL_VENDOR
:
2101 case LOCAL_GL_VERSION
: {
2102 const auto raw
= (const char*)gl
->fGetString(pname
);
2103 return FromRaw(raw
);
2106 case dom::MOZ_debug_Binding::WSI_INFO
: {
2108 gl
->GetWSIInfo(&info
);
2109 return Some(std::string(info
.BeginReading()));
2113 ErrorInvalidEnumArg("pname", pname
);
2118 // ---------------------------------
2120 Maybe
<webgl::IndexedName
> webgl::ParseIndexed(const std::string
& str
) {
2121 static const std::regex
kRegex("(.*)\\[([0-9]+)\\]");
2124 if (!std::regex_match(str
, match
, kRegex
)) return {};
2126 const auto index
= std::stoull(match
[2]);
2127 return Some(webgl::IndexedName
{match
[1], index
});
2130 // ExplodeName("foo.bar[3].x") -> ["foo", ".", "bar", "[", "3", "]", ".", "x"]
2131 static std::vector
<std::string
> ExplodeName(const std::string
& str
) {
2132 std::vector
<std::string
> ret
;
2134 static const std::regex
kSep("[.[\\]]");
2136 auto itr
= std::regex_token_iterator
<decltype(str
.begin())>(
2137 str
.begin(), str
.end(), kSep
, {-1, 0});
2138 const auto end
= decltype(itr
)();
2140 for (; itr
!= end
; ++itr
) {
2141 const auto& part
= itr
->str();
2143 ret
.push_back(part
);
2151 // #define DUMP_MakeLinkResult
2153 webgl::LinkActiveInfo
GetLinkActiveInfo(
2154 gl::GLContext
& gl
, const GLuint prog
, const bool webgl2
,
2155 const std::unordered_map
<std::string
, std::string
>& nameUnmap
) {
2156 webgl::LinkActiveInfo ret
;
2158 const auto fnGetProgramui
= [&](const GLenum pname
) {
2160 gl
.fGetProgramiv(prog
, pname
, &ret
);
2161 return static_cast<uint32_t>(ret
);
2164 std::vector
<char> stringBuffer(1);
2165 const auto fnEnsureCapacity
= [&](const GLenum pname
) {
2166 const auto maxWithNull
= fnGetProgramui(pname
);
2167 if (maxWithNull
> stringBuffer
.size()) {
2168 stringBuffer
.resize(maxWithNull
);
2172 fnEnsureCapacity(LOCAL_GL_ACTIVE_ATTRIBUTE_MAX_LENGTH
);
2173 fnEnsureCapacity(LOCAL_GL_ACTIVE_UNIFORM_MAX_LENGTH
);
2175 fnEnsureCapacity(LOCAL_GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH
);
2176 fnEnsureCapacity(LOCAL_GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH
);
2181 const auto fnUnmapName
= [&](const std::string
& mappedName
) {
2182 const auto parts
= ExplodeName(mappedName
);
2184 std::ostringstream ret
;
2185 for (const auto& part
: parts
) {
2186 const auto maybe
= MaybeFind(nameUnmap
, part
);
2199 const auto count
= fnGetProgramui(LOCAL_GL_ACTIVE_ATTRIBUTES
);
2200 ret
.activeAttribs
.reserve(count
);
2201 for (const auto i
: IntegerRange(count
)) {
2202 GLsizei lengthWithoutNull
= 0;
2203 GLint elemCount
= 0; // `size`
2204 GLenum elemType
= 0; // `type`
2205 gl
.fGetActiveAttrib(prog
, i
, stringBuffer
.size(), &lengthWithoutNull
,
2206 &elemCount
, &elemType
, stringBuffer
.data());
2208 const auto error
= gl
.fGetError();
2209 if (error
!= LOCAL_GL_CONTEXT_LOST
) {
2210 gfxCriticalError() << "Failed to do glGetActiveAttrib: " << error
;
2214 const auto mappedName
=
2215 std::string(stringBuffer
.data(), lengthWithoutNull
);
2216 const auto userName
= fnUnmapName(mappedName
);
2218 auto loc
= gl
.fGetAttribLocation(prog
, mappedName
.c_str());
2219 if (mappedName
.find("gl_") == 0) {
2220 // Bug 1328559: Appears problematic on ANGLE and OSX, but not Linux or
2225 #ifdef DUMP_MakeLinkResult
2226 printf_stderr("[attrib %u/%u] @%i %s->%s\n", i
, count
, loc
,
2227 userName
.c_str(), mappedName
.c_str());
2229 webgl::ActiveAttribInfo info
;
2230 info
.elemType
= elemType
;
2231 info
.elemCount
= elemCount
;
2232 info
.name
= userName
;
2233 info
.location
= loc
;
2234 info
.baseType
= webgl::ToAttribBaseType(info
.elemType
);
2235 ret
.activeAttribs
.push_back(std::move(info
));
2242 const auto count
= fnGetProgramui(LOCAL_GL_ACTIVE_UNIFORMS
);
2243 ret
.activeUniforms
.reserve(count
);
2245 std::vector
<GLint
> blockIndexList(count
, -1);
2246 std::vector
<GLint
> blockOffsetList(count
, -1);
2247 std::vector
<GLint
> blockArrayStrideList(count
, -1);
2248 std::vector
<GLint
> blockMatrixStrideList(count
, -1);
2249 std::vector
<GLint
> blockIsRowMajorList(count
, 0);
2251 if (webgl2
&& count
) {
2252 std::vector
<GLuint
> activeIndices
;
2253 activeIndices
.reserve(count
);
2254 for (const auto i
: IntegerRange(count
)) {
2255 activeIndices
.push_back(i
);
2258 gl
.fGetActiveUniformsiv(
2259 prog
, activeIndices
.size(), activeIndices
.data(),
2260 LOCAL_GL_UNIFORM_BLOCK_INDEX
, blockIndexList
.data());
2262 gl
.fGetActiveUniformsiv(prog
, activeIndices
.size(),
2263 activeIndices
.data(), LOCAL_GL_UNIFORM_OFFSET
,
2264 blockOffsetList
.data());
2266 gl
.fGetActiveUniformsiv(
2267 prog
, activeIndices
.size(), activeIndices
.data(),
2268 LOCAL_GL_UNIFORM_ARRAY_STRIDE
, blockArrayStrideList
.data());
2270 gl
.fGetActiveUniformsiv(
2271 prog
, activeIndices
.size(), activeIndices
.data(),
2272 LOCAL_GL_UNIFORM_MATRIX_STRIDE
, blockMatrixStrideList
.data());
2274 gl
.fGetActiveUniformsiv(
2275 prog
, activeIndices
.size(), activeIndices
.data(),
2276 LOCAL_GL_UNIFORM_IS_ROW_MAJOR
, blockIsRowMajorList
.data());
2279 for (const auto i
: IntegerRange(count
)) {
2280 GLsizei lengthWithoutNull
= 0;
2281 GLint elemCount
= 0; // `size`
2282 GLenum elemType
= 0; // `type`
2283 gl
.fGetActiveUniform(prog
, i
, stringBuffer
.size(), &lengthWithoutNull
,
2284 &elemCount
, &elemType
, stringBuffer
.data());
2286 const auto error
= gl
.fGetError();
2287 if (error
!= LOCAL_GL_CONTEXT_LOST
) {
2288 gfxCriticalError() << "Failed to do glGetActiveUniform: " << error
;
2292 auto mappedName
= std::string(stringBuffer
.data(), lengthWithoutNull
);
2296 auto baseMappedName
= mappedName
;
2298 const bool isArray
= [&]() {
2299 const auto maybe
= webgl::ParseIndexed(mappedName
);
2301 MOZ_ASSERT(maybe
->index
== 0);
2302 baseMappedName
= std::move(maybe
->name
);
2308 const auto userName
= fnUnmapName(mappedName
);
2309 if (StartsWith(userName
, "webgl_")) continue;
2313 webgl::ActiveUniformInfo info
;
2314 info
.elemType
= elemType
;
2315 info
.elemCount
= static_cast<uint32_t>(elemCount
);
2316 info
.name
= userName
;
2317 info
.block_index
= blockIndexList
[i
];
2318 info
.block_offset
= blockOffsetList
[i
];
2319 info
.block_arrayStride
= blockArrayStrideList
[i
];
2320 info
.block_matrixStride
= blockMatrixStrideList
[i
];
2321 info
.block_isRowMajor
= bool(blockIsRowMajorList
[i
]);
2323 #ifdef DUMP_MakeLinkResult
2324 printf_stderr("[uniform %u/%u] %s->%s\n", i
+ 1, count
,
2325 userName
.c_str(), mappedName
.c_str());
2328 // Get uniform locations
2330 auto locName
= baseMappedName
;
2331 const auto baseLength
= locName
.size();
2332 for (const auto i
: IntegerRange(info
.elemCount
)) {
2335 baseLength
); // Erase previous [N], but retain capacity.
2337 locName
+= std::to_string(i
);
2340 const auto loc
= gl
.fGetUniformLocation(prog
, locName
.c_str());
2342 info
.locByIndex
[i
] = static_cast<uint32_t>(loc
);
2343 #ifdef DUMP_MakeLinkResult
2344 printf_stderr(" [%u] @%i\n", i
, loc
);
2350 ret
.activeUniforms
.push_back(std::move(info
));
2355 // -------------------------------------
2356 // active uniform blocks
2358 const auto count
= fnGetProgramui(LOCAL_GL_ACTIVE_UNIFORM_BLOCKS
);
2359 ret
.activeUniformBlocks
.reserve(count
);
2361 for (const auto i
: IntegerRange(count
)) {
2362 GLsizei lengthWithoutNull
= 0;
2363 gl
.fGetActiveUniformBlockName(prog
, i
, stringBuffer
.size(),
2365 stringBuffer
.data());
2366 const auto mappedName
=
2367 std::string(stringBuffer
.data(), lengthWithoutNull
);
2368 const auto userName
= fnUnmapName(mappedName
);
2372 auto info
= webgl::ActiveUniformBlockInfo
{userName
};
2375 gl
.fGetActiveUniformBlockiv(prog
, i
, LOCAL_GL_UNIFORM_BLOCK_DATA_SIZE
,
2377 info
.dataSize
= static_cast<uint32_t>(val
);
2379 gl
.fGetActiveUniformBlockiv(
2380 prog
, i
, LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS
, &val
);
2381 info
.activeUniformIndices
.resize(val
);
2382 gl
.fGetActiveUniformBlockiv(
2383 prog
, i
, LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES
,
2384 reinterpret_cast<GLint
*>(info
.activeUniformIndices
.data()));
2386 gl
.fGetActiveUniformBlockiv(
2387 prog
, i
, LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER
,
2389 info
.referencedByVertexShader
= bool(val
);
2391 gl
.fGetActiveUniformBlockiv(
2392 prog
, i
, LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER
,
2394 info
.referencedByFragmentShader
= bool(val
);
2396 ret
.activeUniformBlocks
.push_back(std::move(info
));
2400 // -------------------------------------
2401 // active tf varyings
2403 const auto count
= fnGetProgramui(LOCAL_GL_TRANSFORM_FEEDBACK_VARYINGS
);
2404 ret
.activeTfVaryings
.reserve(count
);
2406 for (const auto i
: IntegerRange(count
)) {
2407 GLsizei lengthWithoutNull
= 0;
2408 GLsizei elemCount
= 0; // `size`
2409 GLenum elemType
= 0; // `type`
2410 gl
.fGetTransformFeedbackVarying(prog
, i
, stringBuffer
.size(),
2411 &lengthWithoutNull
, &elemCount
,
2412 &elemType
, stringBuffer
.data());
2413 const auto mappedName
=
2414 std::string(stringBuffer
.data(), lengthWithoutNull
);
2415 const auto userName
= fnUnmapName(mappedName
);
2417 ret
.activeTfVaryings
.push_back(
2418 {elemType
, static_cast<uint32_t>(elemCount
), userName
});
2426 nsCString
ToCString(const std::string
& s
) {
2427 return nsCString(s
.data(), s
.size());
2430 webgl::CompileResult
WebGLContext::GetCompileResult(
2431 const WebGLShader
& shader
) const {
2432 webgl::CompileResult ret
;
2434 ret
.pending
= false;
2435 const auto& info
= shader
.CompileResults();
2437 if (!info
->mValid
) {
2438 ret
.log
= info
->mInfoLog
.c_str();
2441 // TODO: These could be large and should be made fallible.
2442 ret
.translatedSource
= ToCString(info
->mObjectCode
);
2443 ret
.log
= ToCString(shader
.CompileLog());
2444 if (!shader
.IsCompiled()) return;
2450 webgl::LinkResult
WebGLContext::GetLinkResult(const WebGLProgram
& prog
) const {
2451 webgl::LinkResult ret
;
2453 ret
.pending
= false; // Link status polling not yet implemented.
2454 ret
.log
= ToCString(prog
.LinkLog());
2455 const auto& info
= prog
.LinkInfo();
2458 ret
.active
= info
->active
;
2459 ret
.tfBufferMode
= info
->transformFeedbackBufferMode
;
2466 GLint
WebGLContext::GetFragDataLocation(const WebGLProgram
& prog
,
2467 const std::string
& userName
) const {
2468 const auto err
= CheckGLSLVariableName(IsWebGL2(), userName
);
2470 GenerateError(err
->type
, "%s", err
->info
.c_str());
2474 const auto& info
= prog
.LinkInfo();
2475 if (!info
) return -1;
2476 const auto& nameMap
= info
->nameMap
;
2478 const auto parts
= ExplodeName(userName
);
2480 std::ostringstream ret
;
2481 for (const auto& part
: parts
) {
2482 const auto maybe
= MaybeFind(nameMap
, part
);
2489 const auto mappedName
= ret
.str();
2491 if (gl
->WorkAroundDriverBugs() && gl
->IsMesa()) {
2492 // Mesa incorrectly generates INVALID_OPERATION for gl_ prefixes here.
2493 if (mappedName
.find("gl_") == 0) {
2498 return gl
->fGetFragDataLocation(prog
.mGLName
, mappedName
.c_str());
2503 WebGLContextBoundObject::WebGLContextBoundObject(WebGLContext
* webgl
)
2504 : mContext(webgl
) {}
2508 Result
<webgl::ExplicitPixelPackingState
, std::string
>
2509 webgl::ExplicitPixelPackingState::ForUseWith(
2510 const webgl::PixelPackingState
& stateOrZero
, const GLenum target
,
2511 const uvec3
& subrectSize
, const webgl::PackingInfo
& pi
,
2512 const Maybe
<size_t> bytesPerRowStrideOverride
) {
2513 auto state
= stateOrZero
;
2515 if (!IsTexTarget3D(target
)) {
2516 state
.skipImages
= 0;
2517 state
.imageHeight
= 0;
2519 if (!state
.rowLength
) {
2520 state
.rowLength
= subrectSize
.x
;
2522 if (!state
.imageHeight
) {
2523 state
.imageHeight
= subrectSize
.y
;
2528 const auto mpii
= PackingInfoInfo::For(pi
);
2531 nsPrintfCString("Invalid pi: { 0x%x, 0x%x}", pi
.format
, pi
.type
);
2532 return Err(mozilla::ToString(text
));
2534 const auto pii
= *mpii
;
2535 const auto bytesPerPixel
= pii
.BytesPerPixel();
2537 const auto ElemsPerRowStride
= [&]() {
2539 // p: `Elem*` pointer to the first element of the first row
2540 // N: row number, starting at 0
2541 // l: groups (pixels) per row
2542 // n: elements per group (pixel) in [1,2,3,4]
2543 // s: bytes per element in [1,2,4,8]
2544 // a: UNPACK_ALIGNMENT in [1,2,4,8]
2545 // Pointer to first element of Nth row: p + N*k
2547 // k(s<a): a/s * ceil(s*n*l/a)
2548 const auto n__elemsPerPixel
= pii
.elementsPerPixel
;
2549 const auto l__pixelsPerRow
= state
.rowLength
;
2550 const auto a__alignment
= state
.alignmentInTypeElems
;
2551 const auto s__bytesPerElem
= pii
.bytesPerElement
;
2553 const auto nl
= CheckedInt
<size_t>(n__elemsPerPixel
) * l__pixelsPerRow
;
2554 auto k__elemsPerRowStride
= nl
;
2555 if (s__bytesPerElem
< a__alignment
) {
2556 // k = a/s * ceil(s*n*l/a)
2557 k__elemsPerRowStride
=
2558 a__alignment
/ s__bytesPerElem
*
2559 ((nl
* s__bytesPerElem
+ a__alignment
- 1) / a__alignment
);
2561 return k__elemsPerRowStride
;
2566 if (bytesPerRowStrideOverride
) { // E.g. HTMLImageElement
2567 const size_t bytesPerRowStrideRequired
= *bytesPerRowStrideOverride
;
2568 // We have to reverse-engineer an ALIGNMENT and ROW_LENGTH for this.
2570 // GL does this in elems not bytes, so we should too.
2571 MOZ_RELEASE_ASSERT(bytesPerRowStrideRequired
% pii
.bytesPerElement
== 0);
2572 const auto elemsPerRowStrideRequired
=
2573 bytesPerRowStrideRequired
/ pii
.bytesPerElement
;
2575 state
.rowLength
= bytesPerRowStrideRequired
/ bytesPerPixel
;
2576 state
.alignmentInTypeElems
= 8;
2578 const auto elemPerRowStride
= ElemsPerRowStride();
2579 if (elemPerRowStride
.isValid() &&
2580 elemPerRowStride
.value() == elemsPerRowStrideRequired
) {
2583 state
.alignmentInTypeElems
/= 2;
2584 if (!state
.alignmentInTypeElems
) {
2585 const auto text
= nsPrintfCString(
2586 "No valid alignment found: pi: { 0x%x, 0x%x},"
2587 " bytesPerRowStrideRequired: %zu",
2588 pi
.format
, pi
.type
, bytesPerRowStrideRequired
);
2589 return Err(mozilla::ToString(text
));
2596 const auto usedPixelsPerRow
=
2597 CheckedInt
<size_t>(state
.skipPixels
) + subrectSize
.x
;
2598 if (!usedPixelsPerRow
.isValid() ||
2599 usedPixelsPerRow
.value() > state
.rowLength
) {
2600 return Err("UNPACK_SKIP_PIXELS + width > UNPACK_ROW_LENGTH.");
2603 if (subrectSize
.y
> state
.imageHeight
) {
2604 return Err("height > UNPACK_IMAGE_HEIGHT.");
2606 // The spec doesn't bound SKIP_ROWS + height <= IMAGE_HEIGHT, unfortunately.
2610 auto metrics
= Metrics
{};
2612 metrics
.usedSize
= subrectSize
;
2613 metrics
.bytesPerPixel
= BytesPerPixel(pi
);
2617 const auto elemsPerRowStride
= ElemsPerRowStride();
2618 const auto bytesPerRowStride
= pii
.bytesPerElement
* elemsPerRowStride
;
2619 if (!bytesPerRowStride
.isValid()) {
2620 return Err("ROW_LENGTH or width too large for packing.");
2622 metrics
.bytesPerRowStride
= bytesPerRowStride
.value();
2626 const auto firstImageTotalRows
=
2627 CheckedInt
<size_t>(state
.skipRows
) + metrics
.usedSize
.y
;
2628 const auto totalImages
=
2629 CheckedInt
<size_t>(state
.skipImages
) + metrics
.usedSize
.z
;
2630 auto totalRows
= CheckedInt
<size_t>(0);
2631 if (metrics
.usedSize
.y
&& metrics
.usedSize
.z
) {
2632 totalRows
= firstImageTotalRows
+ state
.imageHeight
* (totalImages
- 1);
2634 if (!totalRows
.isValid()) {
2636 "SKIP_ROWS, height, IMAGE_HEIGHT, SKIP_IMAGES, or depth too large for "
2639 metrics
.totalRows
= totalRows
.value();
2643 const auto totalBytesStrided
= totalRows
* metrics
.bytesPerRowStride
;
2644 if (!totalBytesStrided
.isValid()) {
2645 return Err("Total byte count too large for packing.");
2647 metrics
.totalBytesStrided
= totalBytesStrided
.value();
2649 metrics
.totalBytesUsed
= metrics
.totalBytesStrided
;
2650 if (metrics
.usedSize
.x
&& metrics
.usedSize
.y
&& metrics
.usedSize
.z
) {
2651 const auto usedBytesPerRow
=
2652 usedPixelsPerRow
.value() * metrics
.bytesPerPixel
;
2653 metrics
.totalBytesUsed
-= metrics
.bytesPerRowStride
;
2654 metrics
.totalBytesUsed
+= usedBytesPerRow
;
2659 return {{state
, metrics
}};
2662 } // namespace mozilla