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