Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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"
7 #include "WebGL1Context.h"
8 #include "WebGLObjectModel.h"
9 #include "WebGLExtensions.h"
10 #include "WebGLContextUtils.h"
11 #include "WebGLBuffer.h"
12 #include "WebGLVertexAttribData.h"
13 #include "WebGLMemoryTracker.h"
14 #include "WebGLFramebuffer.h"
15 #include "WebGLVertexArray.h"
16 #include "WebGLQuery.h"
18 #include "AccessCheck.h"
19 #include "nsIConsoleService.h"
20 #include "nsServiceManagerUtils.h"
21 #include "nsIClassInfoImpl.h"
22 #include "nsContentUtils.h"
23 #include "nsIXPConnect.h"
24 #include "nsError.h"
25 #include "nsIGfxInfo.h"
26 #include "nsIWidget.h"
28 #include "nsIVariant.h"
30 #include "ImageEncoder.h"
32 #include "gfxContext.h"
33 #include "gfxPattern.h"
34 #include "gfxUtils.h"
36 #include "CanvasUtils.h"
37 #include "nsDisplayList.h"
39 #include "GLContextProvider.h"
40 #include "GLContext.h"
41 #include "ScopedGLHelpers.h"
42 #include "GLReadTexImageHelper.h"
44 #include "gfxCrashReporterUtils.h"
46 #include "nsSVGEffects.h"
48 #include "prenv.h"
50 #include "mozilla/Preferences.h"
51 #include "mozilla/Services.h"
52 #include "mozilla/Telemetry.h"
54 #include "nsIObserverService.h"
55 #include "mozilla/Services.h"
56 #include "mozilla/dom/WebGLRenderingContextBinding.h"
57 #include "mozilla/dom/BindingUtils.h"
58 #include "mozilla/dom/ImageData.h"
59 #include "mozilla/ProcessPriorityManager.h"
60 #include "mozilla/EnumeratedArrayCycleCollection.h"
62 #include "Layers.h"
64 #ifdef MOZ_WIDGET_GONK
65 #include "mozilla/layers/ShadowLayers.h"
66 #endif
68 using namespace mozilla;
69 using namespace mozilla::dom;
70 using namespace mozilla::gfx;
71 using namespace mozilla::gl;
72 using namespace mozilla::layers;
74 NS_IMETHODIMP
75 WebGLMemoryPressureObserver::Observe(nsISupports* aSubject,
76 const char* aTopic,
77 const char16_t* aSomeData)
78 {
79 if (strcmp(aTopic, "memory-pressure"))
80 return NS_OK;
82 bool wantToLoseContext = true;
84 if (!mContext->mCanLoseContextInForeground &&
85 ProcessPriorityManager::CurrentProcessIsForeground())
86 wantToLoseContext = false;
87 else if (!nsCRT::strcmp(aSomeData,
88 MOZ_UTF16("heap-minimize")))
89 wantToLoseContext = mContext->mLoseContextOnHeapMinimize;
91 if (wantToLoseContext)
92 mContext->ForceLoseContext();
94 return NS_OK;
95 }
98 WebGLContextOptions::WebGLContextOptions()
99 : alpha(true), depth(true), stencil(false),
100 premultipliedAlpha(true), antialias(true),
101 preserveDrawingBuffer(false)
102 {
103 // Set default alpha state based on preference.
104 if (Preferences::GetBool("webgl.default-no-alpha", false))
105 alpha = false;
106 }
108 WebGLContext::WebGLContext()
109 : gl(nullptr)
110 {
111 SetIsDOMBinding();
113 mGeneration = 0;
114 mInvalidated = false;
115 mShouldPresent = true;
116 mResetLayer = true;
117 mOptionsFrozen = false;
119 mActiveTexture = 0;
120 mPixelStoreFlipY = false;
121 mPixelStorePremultiplyAlpha = false;
122 mPixelStoreColorspaceConversion = BROWSER_DEFAULT_WEBGL;
124 mShaderValidation = true;
126 mFakeBlackStatus = WebGLContextFakeBlackStatus::NotNeeded;
128 mVertexAttrib0Vector[0] = 0;
129 mVertexAttrib0Vector[1] = 0;
130 mVertexAttrib0Vector[2] = 0;
131 mVertexAttrib0Vector[3] = 1;
132 mFakeVertexAttrib0BufferObjectVector[0] = 0;
133 mFakeVertexAttrib0BufferObjectVector[1] = 0;
134 mFakeVertexAttrib0BufferObjectVector[2] = 0;
135 mFakeVertexAttrib0BufferObjectVector[3] = 1;
136 mFakeVertexAttrib0BufferObjectSize = 0;
137 mFakeVertexAttrib0BufferObject = 0;
138 mFakeVertexAttrib0BufferStatus = WebGLVertexAttrib0Status::Default;
140 mViewportX = 0;
141 mViewportY = 0;
142 mViewportWidth = 0;
143 mViewportHeight = 0;
145 mScissorTestEnabled = 0;
146 mDitherEnabled = 1;
147 mRasterizerDiscardEnabled = 0; // OpenGL ES 3.0 spec p244
149 // initialize some GL values: we're going to get them from the GL and use them as the sizes of arrays,
150 // so in case glGetIntegerv leaves them uninitialized because of a GL bug, we would have very weird crashes.
151 mGLMaxVertexAttribs = 0;
152 mGLMaxTextureUnits = 0;
153 mGLMaxTextureSize = 0;
154 mGLMaxCubeMapTextureSize = 0;
155 mGLMaxRenderbufferSize = 0;
156 mGLMaxTextureImageUnits = 0;
157 mGLMaxVertexTextureImageUnits = 0;
158 mGLMaxVaryingVectors = 0;
159 mGLMaxFragmentUniformVectors = 0;
160 mGLMaxVertexUniformVectors = 0;
161 mGLMaxColorAttachments = 1;
162 mGLMaxDrawBuffers = 1;
163 mGLMaxTransformFeedbackSeparateAttribs = 0;
165 // See OpenGL ES 2.0.25 spec, 6.2 State Tables, table 6.13
166 mPixelStorePackAlignment = 4;
167 mPixelStoreUnpackAlignment = 4;
169 WebGLMemoryTracker::AddWebGLContext(this);
171 mAllowRestore = true;
172 mContextLossTimerRunning = false;
173 mDrawSinceContextLossTimerSet = false;
174 mContextRestorer = do_CreateInstance("@mozilla.org/timer;1");
175 mContextStatus = ContextNotLost;
176 mContextLostErrorSet = false;
177 mLoseContextOnHeapMinimize = false;
178 mCanLoseContextInForeground = true;
180 mAlreadyGeneratedWarnings = 0;
181 mAlreadyWarnedAboutFakeVertexAttrib0 = false;
182 mAlreadyWarnedAboutViewportLargerThanDest = false;
183 mMaxWarnings = Preferences::GetInt("webgl.max-warnings-per-context", 32);
184 if (mMaxWarnings < -1)
185 {
186 GenerateWarning("webgl.max-warnings-per-context size is too large (seems like a negative value wrapped)");
187 mMaxWarnings = 0;
188 }
190 mLastUseIndex = 0;
192 InvalidateBufferFetching();
194 mBackbufferNeedsClear = true;
196 mDisableFragHighP = false;
198 mDrawCallsSinceLastFlush = 0;
199 }
201 WebGLContext::~WebGLContext()
202 {
203 DestroyResourcesAndContext();
204 WebGLMemoryTracker::RemoveWebGLContext(this);
205 TerminateContextLossTimer();
206 mContextRestorer = nullptr;
207 }
209 void
210 WebGLContext::DestroyResourcesAndContext()
211 {
212 if (mMemoryPressureObserver) {
213 nsCOMPtr<nsIObserverService> observerService
214 = mozilla::services::GetObserverService();
215 if (observerService) {
216 observerService->RemoveObserver(mMemoryPressureObserver,
217 "memory-pressure");
218 }
219 mMemoryPressureObserver = nullptr;
220 }
222 if (!gl)
223 return;
225 gl->MakeCurrent();
227 mBound2DTextures.Clear();
228 mBoundCubeMapTextures.Clear();
229 mBoundArrayBuffer = nullptr;
230 mBoundTransformFeedbackBuffer = nullptr;
231 mCurrentProgram = nullptr;
232 mBoundFramebuffer = nullptr;
233 mActiveOcclusionQuery = nullptr;
234 mBoundRenderbuffer = nullptr;
235 mBoundVertexArray = nullptr;
236 mDefaultVertexArray = nullptr;
238 while (!mTextures.isEmpty())
239 mTextures.getLast()->DeleteOnce();
240 while (!mVertexArrays.isEmpty())
241 mVertexArrays.getLast()->DeleteOnce();
242 while (!mBuffers.isEmpty())
243 mBuffers.getLast()->DeleteOnce();
244 while (!mRenderbuffers.isEmpty())
245 mRenderbuffers.getLast()->DeleteOnce();
246 while (!mFramebuffers.isEmpty())
247 mFramebuffers.getLast()->DeleteOnce();
248 while (!mShaders.isEmpty())
249 mShaders.getLast()->DeleteOnce();
250 while (!mPrograms.isEmpty())
251 mPrograms.getLast()->DeleteOnce();
252 while (!mQueries.isEmpty())
253 mQueries.getLast()->DeleteOnce();
255 mBlackOpaqueTexture2D = nullptr;
256 mBlackOpaqueTextureCubeMap = nullptr;
257 mBlackTransparentTexture2D = nullptr;
258 mBlackTransparentTextureCubeMap = nullptr;
260 if (mFakeVertexAttrib0BufferObject) {
261 gl->fDeleteBuffers(1, &mFakeVertexAttrib0BufferObject);
262 }
264 // disable all extensions except "WEBGL_lose_context". see bug #927969
265 // spec: http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
266 for (size_t i = 0; i < size_t(WebGLExtensionID::Max); ++i) {
267 WebGLExtensionID extension = WebGLExtensionID(i);
269 if (!IsExtensionEnabled(extension) || (extension == WebGLExtensionID::WEBGL_lose_context))
270 continue;
272 mExtensions[extension]->MarkLost();
273 mExtensions[extension] = nullptr;
274 }
276 // We just got rid of everything, so the context had better
277 // have been going away.
278 #ifdef DEBUG
279 if (gl->DebugMode()) {
280 printf_stderr("--- WebGL context destroyed: %p\n", gl.get());
281 }
282 #endif
284 gl = nullptr;
285 }
287 void
288 WebGLContext::Invalidate()
289 {
290 if (mInvalidated)
291 return;
293 if (!mCanvasElement)
294 return;
296 nsSVGEffects::InvalidateDirectRenderingObservers(mCanvasElement);
298 mInvalidated = true;
299 mCanvasElement->InvalidateCanvasContent(nullptr);
300 }
302 //
303 // nsICanvasRenderingContextInternal
304 //
306 NS_IMETHODIMP
307 WebGLContext::SetContextOptions(JSContext* aCx, JS::Handle<JS::Value> aOptions)
308 {
309 if (aOptions.isNullOrUndefined() && mOptionsFrozen) {
310 return NS_OK;
311 }
313 WebGLContextAttributes attributes;
314 NS_ENSURE_TRUE(attributes.Init(aCx, aOptions), NS_ERROR_UNEXPECTED);
316 WebGLContextOptions newOpts;
318 newOpts.stencil = attributes.mStencil;
319 newOpts.depth = attributes.mDepth;
320 newOpts.premultipliedAlpha = attributes.mPremultipliedAlpha;
321 newOpts.antialias = attributes.mAntialias;
322 newOpts.preserveDrawingBuffer = attributes.mPreserveDrawingBuffer;
323 if (attributes.mAlpha.WasPassed()) {
324 newOpts.alpha = attributes.mAlpha.Value();
325 }
327 // enforce that if stencil is specified, we also give back depth
328 newOpts.depth |= newOpts.stencil;
330 #if 0
331 GenerateWarning("aaHint: %d stencil: %d depth: %d alpha: %d premult: %d preserve: %d\n",
332 newOpts.antialias ? 1 : 0,
333 newOpts.stencil ? 1 : 0,
334 newOpts.depth ? 1 : 0,
335 newOpts.alpha ? 1 : 0,
336 newOpts.premultipliedAlpha ? 1 : 0,
337 newOpts.preserveDrawingBuffer ? 1 : 0);
338 #endif
340 if (mOptionsFrozen && newOpts != mOptions) {
341 // Error if the options are already frozen, and the ones that were asked for
342 // aren't the same as what they were originally.
343 return NS_ERROR_FAILURE;
344 }
346 mOptions = newOpts;
347 return NS_OK;
348 }
350 #ifdef DEBUG
351 int32_t
352 WebGLContext::GetWidth() const
353 {
354 return mWidth;
355 }
357 int32_t
358 WebGLContext::GetHeight() const
359 {
360 return mHeight;
361 }
362 #endif
364 NS_IMETHODIMP
365 WebGLContext::SetDimensions(int32_t width, int32_t height)
366 {
367 // Early error return cases
369 if (width < 0 || height < 0) {
370 GenerateWarning("Canvas size is too large (seems like a negative value wrapped)");
371 return NS_ERROR_OUT_OF_MEMORY;
372 }
374 if (!GetCanvas())
375 return NS_ERROR_FAILURE;
377 // Early success return cases
379 GetCanvas()->InvalidateCanvas();
381 if (gl && mWidth == width && mHeight == height)
382 return NS_OK;
384 // Zero-sized surfaces can cause problems.
385 if (width == 0) {
386 width = 1;
387 }
388 if (height == 0) {
389 height = 1;
390 }
392 // If we already have a gl context, then we just need to resize it
393 if (gl) {
394 MakeContextCurrent();
396 // If we've already drawn, we should commit the current buffer.
397 PresentScreenBuffer();
399 // ResizeOffscreen scraps the current prod buffer before making a new one.
400 gl->ResizeOffscreen(gfx::IntSize(width, height)); // Doesn't matter if it succeeds (soft-fail)
401 // It's unlikely that we'll get a proper-sized context if we recreate if we didn't on resize
403 // everything's good, we're done here
404 mWidth = gl->OffscreenSize().width;
405 mHeight = gl->OffscreenSize().height;
406 mResetLayer = true;
408 mBackbufferNeedsClear = true;
410 return NS_OK;
411 }
413 // End of early return cases.
414 // At this point we know that we're not just resizing an existing context,
415 // we are initializing a new context.
417 // if we exceeded either the global or the per-principal limit for WebGL contexts,
418 // lose the oldest-used context now to free resources. Note that we can't do that
419 // in the WebGLContext constructor as we don't have a canvas element yet there.
420 // Here is the right place to do so, as we are about to create the OpenGL context
421 // and that is what can fail if we already have too many.
422 LoseOldestWebGLContextIfLimitExceeded();
424 // Get some prefs for some preferred/overriden things
425 NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_FAILURE);
427 #ifdef XP_WIN
428 bool preferEGL =
429 Preferences::GetBool("webgl.prefer-egl", false);
430 bool preferOpenGL =
431 Preferences::GetBool("webgl.prefer-native-gl", false);
432 #endif
433 bool forceEnabled =
434 Preferences::GetBool("webgl.force-enabled", false);
435 bool disabled =
436 Preferences::GetBool("webgl.disabled", false);
437 bool prefer16bit =
438 Preferences::GetBool("webgl.prefer-16bpp", false);
440 ScopedGfxFeatureReporter reporter("WebGL", forceEnabled);
442 if (disabled)
443 return NS_ERROR_FAILURE;
445 // We're going to create an entirely new context. If our
446 // generation is not 0 right now (that is, if this isn't the first
447 // context we're creating), we may have to dispatch a context lost
448 // event.
450 // If incrementing the generation would cause overflow,
451 // don't allow it. Allowing this would allow us to use
452 // resource handles created from older context generations.
453 if (!(mGeneration + 1).isValid())
454 return NS_ERROR_FAILURE; // exit without changing the value of mGeneration
456 SurfaceCaps caps;
458 caps.color = true;
459 caps.alpha = mOptions.alpha;
460 caps.depth = mOptions.depth;
461 caps.stencil = mOptions.stencil;
463 // we should really have this behind a
464 // |gfxPlatform::GetPlatform()->GetScreenDepth() == 16| check, but
465 // for now it's just behind a pref for testing/evaluation.
466 caps.bpp16 = prefer16bit;
468 caps.preserve = mOptions.preserveDrawingBuffer;
470 #ifdef MOZ_WIDGET_GONK
471 nsIWidget *docWidget = nsContentUtils::WidgetForDocument(mCanvasElement->OwnerDoc());
472 if (docWidget) {
473 layers::LayerManager *layerManager = docWidget->GetLayerManager();
474 if (layerManager) {
475 // XXX we really want "AsSurfaceAllocator" here for generality
476 layers::ShadowLayerForwarder *forwarder = layerManager->AsShadowForwarder();
477 if (forwarder) {
478 caps.surfaceAllocator = static_cast<layers::ISurfaceAllocator*>(forwarder);
479 }
480 }
481 }
482 #endif
484 bool forceMSAA =
485 Preferences::GetBool("webgl.msaa-force", false);
487 int32_t status;
488 nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
489 if (mOptions.antialias &&
490 gfxInfo &&
491 NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBGL_MSAA, &status))) {
492 if (status == nsIGfxInfo::FEATURE_NO_INFO || forceMSAA) {
493 caps.antialias = true;
494 }
495 }
497 #ifdef XP_WIN
498 if (PR_GetEnv("MOZ_WEBGL_PREFER_EGL")) {
499 preferEGL = true;
500 }
501 #endif
503 // Ask GfxInfo about what we should use
504 bool useOpenGL = true;
506 #ifdef XP_WIN
507 bool useANGLE = true;
508 #endif
510 if (gfxInfo && !forceEnabled) {
511 if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBGL_OPENGL, &status))) {
512 if (status != nsIGfxInfo::FEATURE_NO_INFO) {
513 useOpenGL = false;
514 }
515 }
516 #ifdef XP_WIN
517 if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBGL_ANGLE, &status))) {
518 if (status != nsIGfxInfo::FEATURE_NO_INFO) {
519 useANGLE = false;
520 }
521 }
522 #endif
523 }
525 #ifdef XP_WIN
526 // allow forcing GL and not EGL/ANGLE
527 if (PR_GetEnv("MOZ_WEBGL_FORCE_OPENGL")) {
528 preferEGL = false;
529 useANGLE = false;
530 useOpenGL = true;
531 }
532 #endif
534 gfxIntSize size(width, height);
536 #ifdef XP_WIN
537 // if we want EGL, try it now
538 if (!gl && (preferEGL || useANGLE) && !preferOpenGL) {
539 gl = gl::GLContextProviderEGL::CreateOffscreen(size, caps);
540 if (!gl || !InitAndValidateGL()) {
541 GenerateWarning("Error during ANGLE OpenGL ES initialization");
542 return NS_ERROR_FAILURE;
543 }
544 }
545 #endif
547 // try the default provider, whatever that is
548 if (!gl && useOpenGL) {
549 gl = gl::GLContextProvider::CreateOffscreen(size, caps);
550 if (gl && !InitAndValidateGL()) {
551 GenerateWarning("Error during OpenGL initialization");
552 return NS_ERROR_FAILURE;
553 }
554 }
556 if (!gl) {
557 GenerateWarning("Can't get a usable WebGL context");
558 return NS_ERROR_FAILURE;
559 }
561 #ifdef DEBUG
562 if (gl->DebugMode()) {
563 printf_stderr("--- WebGL context created: %p\n", gl.get());
564 }
565 #endif
567 mWidth = width;
568 mHeight = height;
569 mViewportWidth = width;
570 mViewportHeight = height;
571 mResetLayer = true;
572 mOptionsFrozen = true;
574 mHasRobustness = gl->HasRobustness();
576 // increment the generation number
577 ++mGeneration;
579 #if 0
580 if (mGeneration > 0) {
581 // XXX dispatch context lost event
582 }
583 #endif
585 MakeContextCurrent();
587 // Make sure that we clear this out, otherwise
588 // we'll end up displaying random memory
589 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
591 gl->fClearColor(0.0f, 0.0f, 0.0f, 0.0f);
592 gl->fClearDepth(1.0f);
593 gl->fClearStencil(0);
595 mBackbufferNeedsClear = true;
597 // Clear immediately, because we need to present the cleared initial
598 // buffer.
599 ClearBackbufferIfNeeded();
601 mShouldPresent = true;
603 MOZ_ASSERT(gl->Caps().color == caps.color);
604 MOZ_ASSERT(gl->Caps().alpha == caps.alpha);
605 MOZ_ASSERT(gl->Caps().depth == caps.depth || !gl->Caps().depth);
606 MOZ_ASSERT(gl->Caps().stencil == caps.stencil || !gl->Caps().stencil);
607 MOZ_ASSERT(gl->Caps().antialias == caps.antialias || !gl->Caps().antialias);
608 MOZ_ASSERT(gl->Caps().preserve == caps.preserve);
610 reporter.SetSuccessful();
611 return NS_OK;
612 }
614 void
615 WebGLContext::ClearBackbufferIfNeeded()
616 {
617 if (!mBackbufferNeedsClear)
618 return;
620 #ifdef DEBUG
621 gl->MakeCurrent();
623 GLuint fb = 0;
624 gl->GetUIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, &fb);
625 MOZ_ASSERT(fb == 0);
626 #endif
628 ClearScreen();
630 mBackbufferNeedsClear = false;
631 }
633 void WebGLContext::LoseOldestWebGLContextIfLimitExceeded()
634 {
635 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
636 // some mobile devices can't have more than 8 GL contexts overall
637 const size_t kMaxWebGLContextsPerPrincipal = 2;
638 const size_t kMaxWebGLContexts = 4;
639 #else
640 const size_t kMaxWebGLContextsPerPrincipal = 16;
641 const size_t kMaxWebGLContexts = 32;
642 #endif
643 MOZ_ASSERT(kMaxWebGLContextsPerPrincipal < kMaxWebGLContexts);
645 // it's important to update the index on a new context before losing old contexts,
646 // otherwise new unused contexts would all have index 0 and we couldn't distinguish older ones
647 // when choosing which one to lose first.
648 UpdateLastUseIndex();
650 WebGLMemoryTracker::ContextsArrayType &contexts
651 = WebGLMemoryTracker::Contexts();
653 // quick exit path, should cover a majority of cases
654 if (contexts.Length() <= kMaxWebGLContextsPerPrincipal) {
655 return;
656 }
658 // note that here by "context" we mean "non-lost context". See the check for
659 // IsContextLost() below. Indeed, the point of this function is to maybe lose
660 // some currently non-lost context.
662 uint64_t oldestIndex = UINT64_MAX;
663 uint64_t oldestIndexThisPrincipal = UINT64_MAX;
664 const WebGLContext *oldestContext = nullptr;
665 const WebGLContext *oldestContextThisPrincipal = nullptr;
666 size_t numContexts = 0;
667 size_t numContextsThisPrincipal = 0;
669 for(size_t i = 0; i < contexts.Length(); ++i) {
671 // don't want to lose ourselves.
672 if (contexts[i] == this)
673 continue;
675 if (contexts[i]->IsContextLost())
676 continue;
678 if (!contexts[i]->GetCanvas()) {
679 // Zombie context: the canvas is already destroyed, but something else
680 // (typically the compositor) is still holding on to the context.
681 // Killing zombies is a no-brainer.
682 const_cast<WebGLContext*>(contexts[i])->LoseContext();
683 continue;
684 }
686 numContexts++;
687 if (contexts[i]->mLastUseIndex < oldestIndex) {
688 oldestIndex = contexts[i]->mLastUseIndex;
689 oldestContext = contexts[i];
690 }
692 nsIPrincipal *ourPrincipal = GetCanvas()->NodePrincipal();
693 nsIPrincipal *theirPrincipal = contexts[i]->GetCanvas()->NodePrincipal();
694 bool samePrincipal;
695 nsresult rv = ourPrincipal->Equals(theirPrincipal, &samePrincipal);
696 if (NS_SUCCEEDED(rv) && samePrincipal) {
697 numContextsThisPrincipal++;
698 if (contexts[i]->mLastUseIndex < oldestIndexThisPrincipal) {
699 oldestIndexThisPrincipal = contexts[i]->mLastUseIndex;
700 oldestContextThisPrincipal = contexts[i];
701 }
702 }
703 }
705 if (numContextsThisPrincipal > kMaxWebGLContextsPerPrincipal) {
706 GenerateWarning("Exceeded %d live WebGL contexts for this principal, losing the "
707 "least recently used one.", kMaxWebGLContextsPerPrincipal);
708 MOZ_ASSERT(oldestContextThisPrincipal); // if we reach this point, this can't be null
709 const_cast<WebGLContext*>(oldestContextThisPrincipal)->LoseContext();
710 } else if (numContexts > kMaxWebGLContexts) {
711 GenerateWarning("Exceeded %d live WebGL contexts, losing the least recently used one.",
712 kMaxWebGLContexts);
713 MOZ_ASSERT(oldestContext); // if we reach this point, this can't be null
714 const_cast<WebGLContext*>(oldestContext)->LoseContext();
715 }
716 }
718 void
719 WebGLContext::GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat)
720 {
721 *aImageBuffer = nullptr;
722 *aFormat = 0;
724 // Use GetSurfaceSnapshot() to make sure that appropriate y-flip gets applied
725 bool premult;
726 RefPtr<SourceSurface> snapshot =
727 GetSurfaceSnapshot(mOptions.premultipliedAlpha ? nullptr : &premult);
728 if (!snapshot) {
729 return;
730 }
731 MOZ_ASSERT(mOptions.premultipliedAlpha || !premult, "We must get unpremult when we ask for it!");
733 RefPtr<DataSourceSurface> dataSurface = snapshot->GetDataSurface();
735 DataSourceSurface::MappedSurface map;
736 if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
737 return;
738 }
740 static const fallible_t fallible = fallible_t();
741 uint8_t* imageBuffer = new (fallible) uint8_t[mWidth * mHeight * 4];
742 if (!imageBuffer) {
743 dataSurface->Unmap();
744 return;
745 }
746 memcpy(imageBuffer, map.mData, mWidth * mHeight * 4);
748 dataSurface->Unmap();
750 int32_t format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
751 if (!mOptions.premultipliedAlpha) {
752 // We need to convert to INPUT_FORMAT_RGBA, otherwise
753 // we are automatically considered premult, and unpremult'd.
754 // Yes, it is THAT silly.
755 // Except for different lossy conversions by color,
756 // we could probably just change the label, and not change the data.
757 gfxUtils::ConvertBGRAtoRGBA(imageBuffer, mWidth * mHeight * 4);
758 format = imgIEncoder::INPUT_FORMAT_RGBA;
759 }
761 *aImageBuffer = imageBuffer;
762 *aFormat = format;
763 }
765 NS_IMETHODIMP
766 WebGLContext::GetInputStream(const char* aMimeType,
767 const char16_t* aEncoderOptions,
768 nsIInputStream **aStream)
769 {
770 NS_ASSERTION(gl, "GetInputStream on invalid context?");
771 if (!gl)
772 return NS_ERROR_FAILURE;
774 nsCString enccid("@mozilla.org/image/encoder;2?type=");
775 enccid += aMimeType;
776 nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
777 if (!encoder) {
778 return NS_ERROR_FAILURE;
779 }
781 nsAutoArrayPtr<uint8_t> imageBuffer;
782 int32_t format = 0;
783 GetImageBuffer(getter_Transfers(imageBuffer), &format);
784 if (!imageBuffer) {
785 return NS_ERROR_FAILURE;
786 }
788 return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer, format,
789 encoder, aEncoderOptions, aStream);
790 }
792 void WebGLContext::UpdateLastUseIndex()
793 {
794 static CheckedInt<uint64_t> sIndex = 0;
796 sIndex++;
798 // should never happen with 64-bit; trying to handle this would be riskier than
799 // not handling it as the handler code would never get exercised.
800 if (!sIndex.isValid()) {
801 NS_RUNTIMEABORT("Can't believe it's been 2^64 transactions already!");
802 }
804 mLastUseIndex = sIndex.value();
805 }
807 static uint8_t gWebGLLayerUserData;
809 namespace mozilla {
811 class WebGLContextUserData : public LayerUserData {
812 public:
813 WebGLContextUserData(HTMLCanvasElement *aContent)
814 : mContent(aContent)
815 {}
817 /* PreTransactionCallback gets called by the Layers code every time the
818 * WebGL canvas is going to be composited.
819 */
820 static void PreTransactionCallback(void* data)
821 {
822 WebGLContextUserData* userdata = static_cast<WebGLContextUserData*>(data);
823 HTMLCanvasElement* canvas = userdata->mContent;
824 WebGLContext* context = static_cast<WebGLContext*>(canvas->GetContextAtIndex(0));
826 // Present our screenbuffer, if needed.
827 context->PresentScreenBuffer();
828 context->mDrawCallsSinceLastFlush = 0;
829 }
831 /** DidTransactionCallback gets called by the Layers code everytime the WebGL canvas gets composite,
832 * so it really is the right place to put actions that have to be performed upon compositing
833 */
834 static void DidTransactionCallback(void* aData)
835 {
836 WebGLContextUserData *userdata = static_cast<WebGLContextUserData*>(aData);
837 HTMLCanvasElement *canvas = userdata->mContent;
838 WebGLContext *context = static_cast<WebGLContext*>(canvas->GetContextAtIndex(0));
840 // Mark ourselves as no longer invalidated.
841 context->MarkContextClean();
843 context->UpdateLastUseIndex();
844 }
846 private:
847 nsRefPtr<HTMLCanvasElement> mContent;
848 };
850 } // end namespace mozilla
852 already_AddRefed<layers::CanvasLayer>
853 WebGLContext::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
854 CanvasLayer *aOldLayer,
855 LayerManager *aManager)
856 {
857 if (IsContextLost())
858 return nullptr;
860 if (!mResetLayer && aOldLayer &&
861 aOldLayer->HasUserData(&gWebGLLayerUserData)) {
862 nsRefPtr<layers::CanvasLayer> ret = aOldLayer;
863 return ret.forget();
864 }
866 nsRefPtr<CanvasLayer> canvasLayer = aManager->CreateCanvasLayer();
867 if (!canvasLayer) {
868 NS_WARNING("CreateCanvasLayer returned null!");
869 return nullptr;
870 }
871 WebGLContextUserData *userData = nullptr;
872 if (aBuilder->IsPaintingToWindow()) {
873 // Make the layer tell us whenever a transaction finishes (including
874 // the current transaction), so we can clear our invalidation state and
875 // start invalidating again. We need to do this for the layer that is
876 // being painted to a window (there shouldn't be more than one at a time,
877 // and if there is, flushing the invalidation state more often than
878 // necessary is harmless).
880 // The layer will be destroyed when we tear down the presentation
881 // (at the latest), at which time this userData will be destroyed,
882 // releasing the reference to the element.
883 // The userData will receive DidTransactionCallbacks, which flush the
884 // the invalidation state to indicate that the canvas is up to date.
885 userData = new WebGLContextUserData(mCanvasElement);
886 canvasLayer->SetDidTransactionCallback(
887 WebGLContextUserData::DidTransactionCallback, userData);
888 canvasLayer->SetPreTransactionCallback(
889 WebGLContextUserData::PreTransactionCallback, userData);
890 }
891 canvasLayer->SetUserData(&gWebGLLayerUserData, userData);
893 CanvasLayer::Data data;
894 data.mGLContext = gl;
895 data.mSize = nsIntSize(mWidth, mHeight);
896 data.mIsGLAlphaPremult = IsPremultAlpha();
898 canvasLayer->Initialize(data);
899 uint32_t flags = gl->Caps().alpha ? 0 : Layer::CONTENT_OPAQUE;
900 canvasLayer->SetContentFlags(flags);
901 canvasLayer->Updated();
903 mResetLayer = false;
905 return canvasLayer.forget();
906 }
908 void
909 WebGLContext::GetContextAttributes(Nullable<dom::WebGLContextAttributes> &retval)
910 {
911 retval.SetNull();
912 if (IsContextLost())
913 return;
915 dom::WebGLContextAttributes& result = retval.SetValue();
917 const PixelBufferFormat& format = gl->GetPixelFormat();
919 result.mAlpha.Construct(format.alpha > 0);
920 result.mDepth = format.depth > 0;
921 result.mStencil = format.stencil > 0;
922 result.mAntialias = format.samples > 1;
923 result.mPremultipliedAlpha = mOptions.premultipliedAlpha;
924 result.mPreserveDrawingBuffer = mOptions.preserveDrawingBuffer;
925 }
927 /* [noscript] DOMString mozGetUnderlyingParamString(in GLenum pname); */
928 NS_IMETHODIMP
929 WebGLContext::MozGetUnderlyingParamString(uint32_t pname, nsAString& retval)
930 {
931 if (IsContextLost())
932 return NS_OK;
934 retval.SetIsVoid(true);
936 MakeContextCurrent();
938 switch (pname) {
939 case LOCAL_GL_VENDOR:
940 case LOCAL_GL_RENDERER:
941 case LOCAL_GL_VERSION:
942 case LOCAL_GL_SHADING_LANGUAGE_VERSION:
943 case LOCAL_GL_EXTENSIONS: {
944 const char *s = (const char *) gl->fGetString(pname);
945 retval.Assign(NS_ConvertASCIItoUTF16(nsDependentCString(s)));
946 }
947 break;
949 default:
950 return NS_ERROR_INVALID_ARG;
951 }
953 return NS_OK;
954 }
956 void
957 WebGLContext::ClearScreen()
958 {
959 bool colorAttachmentsMask[WebGLContext::sMaxColorAttachments] = {false};
961 MakeContextCurrent();
962 ScopedBindFramebuffer autoFB(gl, 0);
964 GLbitfield clearMask = LOCAL_GL_COLOR_BUFFER_BIT;
965 if (mOptions.depth)
966 clearMask |= LOCAL_GL_DEPTH_BUFFER_BIT;
967 if (mOptions.stencil)
968 clearMask |= LOCAL_GL_STENCIL_BUFFER_BIT;
970 colorAttachmentsMask[0] = true;
972 ForceClearFramebufferWithDefaultValues(clearMask, colorAttachmentsMask);
973 }
975 #ifdef DEBUG
976 // For NaNs, etc.
977 static bool IsShadowCorrect(float shadow, float actual) {
978 if (IsNaN(shadow)) {
979 // GL is allowed to do anything it wants for NaNs, so if we're shadowing
980 // a NaN, then whatever `actual` is might be correct.
981 return true;
982 }
984 return shadow == actual;
985 }
986 #endif
988 void
989 WebGLContext::ForceClearFramebufferWithDefaultValues(GLbitfield mask, const bool colorAttachmentsMask[sMaxColorAttachments])
990 {
991 MakeContextCurrent();
993 bool initializeColorBuffer = 0 != (mask & LOCAL_GL_COLOR_BUFFER_BIT);
994 bool initializeDepthBuffer = 0 != (mask & LOCAL_GL_DEPTH_BUFFER_BIT);
995 bool initializeStencilBuffer = 0 != (mask & LOCAL_GL_STENCIL_BUFFER_BIT);
996 bool drawBuffersIsEnabled = IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers);
998 GLenum currentDrawBuffers[WebGLContext::sMaxColorAttachments];
1000 // Fun GL fact: No need to worry about the viewport here, glViewport is just
1001 // setting up a coordinates transformation, it doesn't affect glClear at all.
1003 #ifdef DEBUG
1004 // Scope to hide our variables.
1005 {
1006 // Sanity-check that all our state is set properly. Otherwise, when we
1007 // reset out state to what we *think* it is, we'll get it wrong.
1009 // Dither shouldn't matter when we're clearing to {0,0,0,0}.
1010 MOZ_ASSERT(gl->fIsEnabled(LOCAL_GL_SCISSOR_TEST) == mScissorTestEnabled);
1012 if (initializeColorBuffer) {
1013 realGLboolean colorWriteMask[4] = {2, 2, 2, 2};
1014 GLfloat colorClearValue[4] = {-1.0f, -1.0f, -1.0f, -1.0f};
1016 gl->fGetBooleanv(LOCAL_GL_COLOR_WRITEMASK, colorWriteMask);
1017 gl->fGetFloatv(LOCAL_GL_COLOR_CLEAR_VALUE, colorClearValue);
1019 MOZ_ASSERT(colorWriteMask[0] == mColorWriteMask[0] &&
1020 colorWriteMask[1] == mColorWriteMask[1] &&
1021 colorWriteMask[2] == mColorWriteMask[2] &&
1022 colorWriteMask[3] == mColorWriteMask[3]);
1023 MOZ_ASSERT(IsShadowCorrect(mColorClearValue[0], colorClearValue[0]) &&
1024 IsShadowCorrect(mColorClearValue[1], colorClearValue[1]) &&
1025 IsShadowCorrect(mColorClearValue[2], colorClearValue[2]) &&
1026 IsShadowCorrect(mColorClearValue[3], colorClearValue[3]));
1027 }
1029 if (initializeDepthBuffer) {
1030 realGLboolean depthWriteMask = 2;
1031 GLfloat depthClearValue = -1.0f;
1034 gl->fGetBooleanv(LOCAL_GL_DEPTH_WRITEMASK, &depthWriteMask);
1035 gl->fGetFloatv(LOCAL_GL_DEPTH_CLEAR_VALUE, &depthClearValue);
1037 MOZ_ASSERT(depthWriteMask == mDepthWriteMask);
1038 MOZ_ASSERT(IsShadowCorrect(mDepthClearValue, depthClearValue));
1039 }
1041 if (initializeStencilBuffer) {
1042 GLuint stencilWriteMaskFront = 0xdeadbad1;
1043 GLuint stencilWriteMaskBack = 0xdeadbad1;
1044 GLuint stencilClearValue = 0xdeadbad1;
1046 gl->GetUIntegerv(LOCAL_GL_STENCIL_WRITEMASK, &stencilWriteMaskFront);
1047 gl->GetUIntegerv(LOCAL_GL_STENCIL_BACK_WRITEMASK, &stencilWriteMaskBack);
1048 gl->GetUIntegerv(LOCAL_GL_STENCIL_CLEAR_VALUE, &stencilClearValue);
1050 GLuint stencilBits = 0;
1051 gl->GetUIntegerv(LOCAL_GL_STENCIL_BITS, &stencilBits);
1052 GLuint stencilMask = (GLuint(1) << stencilBits) - 1;
1054 MOZ_ASSERT( ( stencilWriteMaskFront & stencilMask) ==
1055 (mStencilWriteMaskFront & stencilMask) );
1056 MOZ_ASSERT( ( stencilWriteMaskBack & stencilMask) ==
1057 (mStencilWriteMaskBack & stencilMask) );
1058 MOZ_ASSERT( ( stencilClearValue & stencilMask) ==
1059 (mStencilClearValue & stencilMask) );
1060 }
1061 }
1062 #endif
1064 // Prepare GL state for clearing.
1065 gl->fDisable(LOCAL_GL_SCISSOR_TEST);
1067 if (initializeColorBuffer) {
1069 if (drawBuffersIsEnabled) {
1071 GLenum drawBuffersCommand[WebGLContext::sMaxColorAttachments] = { LOCAL_GL_NONE };
1073 for(int32_t i = 0; i < mGLMaxDrawBuffers; i++) {
1074 GLint temp;
1075 gl->fGetIntegerv(LOCAL_GL_DRAW_BUFFER0 + i, &temp);
1076 currentDrawBuffers[i] = temp;
1078 if (colorAttachmentsMask[i]) {
1079 drawBuffersCommand[i] = LOCAL_GL_COLOR_ATTACHMENT0 + i;
1080 }
1081 }
1083 gl->fDrawBuffers(mGLMaxDrawBuffers, drawBuffersCommand);
1084 }
1086 gl->fColorMask(1, 1, 1, 1);
1087 gl->fClearColor(0.0f, 0.0f, 0.0f, 0.0f);
1088 }
1090 if (initializeDepthBuffer) {
1091 gl->fDepthMask(1);
1092 gl->fClearDepth(1.0f);
1093 }
1095 if (initializeStencilBuffer) {
1096 // "The clear operation always uses the front stencil write mask
1097 // when clearing the stencil buffer."
1098 gl->fStencilMaskSeparate(LOCAL_GL_FRONT, 0xffffffff);
1099 gl->fStencilMaskSeparate(LOCAL_GL_BACK, 0xffffffff);
1100 gl->fClearStencil(0);
1101 }
1103 if (mRasterizerDiscardEnabled) {
1104 gl->fDisable(LOCAL_GL_RASTERIZER_DISCARD);
1105 }
1107 // Do the clear!
1108 gl->fClear(mask);
1110 // And reset!
1111 if (mScissorTestEnabled)
1112 gl->fEnable(LOCAL_GL_SCISSOR_TEST);
1114 if (mRasterizerDiscardEnabled) {
1115 gl->fEnable(LOCAL_GL_RASTERIZER_DISCARD);
1116 }
1118 // Restore GL state after clearing.
1119 if (initializeColorBuffer) {
1120 if (drawBuffersIsEnabled) {
1121 gl->fDrawBuffers(mGLMaxDrawBuffers, currentDrawBuffers);
1122 }
1124 gl->fColorMask(mColorWriteMask[0],
1125 mColorWriteMask[1],
1126 mColorWriteMask[2],
1127 mColorWriteMask[3]);
1128 gl->fClearColor(mColorClearValue[0],
1129 mColorClearValue[1],
1130 mColorClearValue[2],
1131 mColorClearValue[3]);
1132 }
1134 if (initializeDepthBuffer) {
1135 gl->fDepthMask(mDepthWriteMask);
1136 gl->fClearDepth(mDepthClearValue);
1137 }
1139 if (initializeStencilBuffer) {
1140 gl->fStencilMaskSeparate(LOCAL_GL_FRONT, mStencilWriteMaskFront);
1141 gl->fStencilMaskSeparate(LOCAL_GL_BACK, mStencilWriteMaskBack);
1142 gl->fClearStencil(mStencilClearValue);
1143 }
1144 }
1146 // For an overview of how WebGL compositing works, see:
1147 // https://wiki.mozilla.org/Platform/GFX/WebGL/Compositing
1148 bool
1149 WebGLContext::PresentScreenBuffer()
1150 {
1151 if (IsContextLost()) {
1152 return false;
1153 }
1155 if (!mShouldPresent) {
1156 return false;
1157 }
1159 gl->MakeCurrent();
1160 MOZ_ASSERT(!mBackbufferNeedsClear);
1161 if (!gl->PublishFrame()) {
1162 this->ForceLoseContext();
1163 return false;
1164 }
1166 if (!mOptions.preserveDrawingBuffer) {
1167 mBackbufferNeedsClear = true;
1168 }
1170 mShouldPresent = false;
1172 return true;
1173 }
1175 void
1176 WebGLContext::DummyFramebufferOperation(const char *info)
1177 {
1178 GLenum status = CheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
1179 if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE)
1180 ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info);
1181 }
1183 // We use this timer for many things. Here are the things that it is activated for:
1184 // 1) If a script is using the MOZ_WEBGL_lose_context extension.
1185 // 2) If we are using EGL and _NOT ANGLE_, we query periodically to see if the
1186 // CONTEXT_LOST_WEBGL error has been triggered.
1187 // 3) If we are using ANGLE, or anything that supports ARB_robustness, query the
1188 // GPU periodically to see if the reset status bit has been set.
1189 // In all of these situations, we use this timer to send the script context lost
1190 // and restored events asynchronously. For example, if it triggers a context loss,
1191 // the webglcontextlost event will be sent to it the next time the robustness timer
1192 // fires.
1193 // Note that this timer mechanism is not used unless one of these 3 criteria
1194 // are met.
1195 // At a bare minimum, from context lost to context restores, it would take 3
1196 // full timer iterations: detection, webglcontextlost, webglcontextrestored.
1197 void
1198 WebGLContext::RobustnessTimerCallback(nsITimer* timer)
1199 {
1200 TerminateContextLossTimer();
1202 if (!mCanvasElement) {
1203 // the canvas is gone. That happens when the page was closed before we got
1204 // this timer event. In this case, there's nothing to do here, just don't crash.
1205 return;
1206 }
1208 // If the context has been lost and we're waiting for it to be restored, do
1209 // that now.
1210 if (mContextStatus == ContextLostAwaitingEvent) {
1211 bool defaultAction;
1212 nsContentUtils::DispatchTrustedEvent(mCanvasElement->OwnerDoc(),
1213 static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
1214 NS_LITERAL_STRING("webglcontextlost"),
1215 true,
1216 true,
1217 &defaultAction);
1219 // If the script didn't handle the event, we don't allow restores.
1220 if (defaultAction)
1221 mAllowRestore = false;
1223 // If the script handled the event and we are allowing restores, then
1224 // mark it to be restored. Otherwise, leave it as context lost
1225 // (unusable).
1226 if (!defaultAction && mAllowRestore) {
1227 ForceRestoreContext();
1228 // Restart the timer so that it will be restored on the next
1229 // callback.
1230 SetupContextLossTimer();
1231 } else {
1232 mContextStatus = ContextLost;
1233 }
1234 } else if (mContextStatus == ContextLostAwaitingRestore) {
1235 // Try to restore the context. If it fails, try again later.
1236 if (NS_FAILED(SetDimensions(mWidth, mHeight))) {
1237 SetupContextLossTimer();
1238 return;
1239 }
1240 mContextStatus = ContextNotLost;
1241 nsContentUtils::DispatchTrustedEvent(mCanvasElement->OwnerDoc(),
1242 static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
1243 NS_LITERAL_STRING("webglcontextrestored"),
1244 true,
1245 true);
1246 // Set all flags back to the state they were in before the context was
1247 // lost.
1248 mEmitContextLostErrorOnce = true;
1249 mAllowRestore = true;
1250 }
1252 MaybeRestoreContext();
1253 return;
1254 }
1256 void
1257 WebGLContext::MaybeRestoreContext()
1258 {
1259 // Don't try to handle it if we already know it's busted.
1260 if (mContextStatus != ContextNotLost || gl == nullptr)
1261 return;
1263 bool isEGL = gl->GetContextType() == gl::GLContextType::EGL,
1264 isANGLE = gl->IsANGLE();
1266 GLContext::ContextResetARB resetStatus = GLContext::CONTEXT_NO_ERROR;
1267 if (mHasRobustness) {
1268 gl->MakeCurrent();
1269 resetStatus = (GLContext::ContextResetARB) gl->fGetGraphicsResetStatus();
1270 } else if (isEGL) {
1271 // Simulate a ARB_robustness guilty context loss for when we
1272 // get an EGL_CONTEXT_LOST error. It may not actually be guilty,
1273 // but we can't make any distinction, so we must assume the worst
1274 // case.
1275 if (!gl->MakeCurrent(true) && gl->IsContextLost()) {
1276 resetStatus = GLContext::CONTEXT_GUILTY_CONTEXT_RESET_ARB;
1277 }
1278 }
1280 if (resetStatus != GLContext::CONTEXT_NO_ERROR) {
1281 // It's already lost, but clean up after it and signal to JS that it is
1282 // lost.
1283 ForceLoseContext();
1284 }
1286 switch (resetStatus) {
1287 case GLContext::CONTEXT_NO_ERROR:
1288 // If there has been activity since the timer was set, it's possible
1289 // that we did or are going to miss something, so clear this flag and
1290 // run it again some time later.
1291 if (mDrawSinceContextLossTimerSet)
1292 SetupContextLossTimer();
1293 break;
1294 case GLContext::CONTEXT_GUILTY_CONTEXT_RESET_ARB:
1295 NS_WARNING("WebGL content on the page caused the graphics card to reset; not restoring the context");
1296 mAllowRestore = false;
1297 break;
1298 case GLContext::CONTEXT_INNOCENT_CONTEXT_RESET_ARB:
1299 break;
1300 case GLContext::CONTEXT_UNKNOWN_CONTEXT_RESET_ARB:
1301 NS_WARNING("WebGL content on the page might have caused the graphics card to reset");
1302 if (isEGL && isANGLE) {
1303 // If we're using ANGLE, we ONLY get back UNKNOWN context resets, including for guilty contexts.
1304 // This means that we can't restore it or risk restoring a guilty context. Should this ever change,
1305 // we can get rid of the whole IsANGLE() junk from GLContext.h since, as of writing, this is the
1306 // only use for it. See ANGLE issue 261.
1307 mAllowRestore = false;
1308 }
1309 break;
1310 }
1311 }
1313 void
1314 WebGLContext::ForceLoseContext()
1315 {
1316 if (mContextStatus == ContextLostAwaitingEvent)
1317 return;
1319 mContextStatus = ContextLostAwaitingEvent;
1320 // Queue up a task to restore the event.
1321 SetupContextLossTimer();
1322 DestroyResourcesAndContext();
1323 }
1325 void
1326 WebGLContext::ForceRestoreContext()
1327 {
1328 mContextStatus = ContextLostAwaitingRestore;
1329 }
1331 void
1332 WebGLContext::MakeContextCurrent() const { gl->MakeCurrent(); }
1334 mozilla::TemporaryRef<mozilla::gfx::SourceSurface>
1335 WebGLContext::GetSurfaceSnapshot(bool* aPremultAlpha)
1336 {
1337 if (!gl)
1338 return nullptr;
1340 nsRefPtr<gfxImageSurface> surf = new gfxImageSurface(gfxIntSize(mWidth, mHeight),
1341 gfxImageFormat::ARGB32,
1342 mWidth * 4, 0, false);
1343 if (surf->CairoStatus() != 0) {
1344 return nullptr;
1345 }
1347 gl->MakeCurrent();
1348 {
1349 ScopedBindFramebuffer autoFB(gl, 0);
1350 ClearBackbufferIfNeeded();
1351 ReadPixelsIntoImageSurface(gl, surf);
1352 }
1354 if (aPremultAlpha) {
1355 *aPremultAlpha = true;
1356 }
1357 bool srcPremultAlpha = mOptions.premultipliedAlpha;
1358 if (!srcPremultAlpha) {
1359 if (aPremultAlpha) {
1360 *aPremultAlpha = false;
1361 } else {
1362 gfxUtils::PremultiplyImageSurface(surf);
1363 surf->MarkDirty();
1364 }
1365 }
1367 RefPtr<DrawTarget> dt =
1368 Factory::CreateDrawTarget(BackendType::CAIRO,
1369 IntSize(mWidth, mHeight),
1370 SurfaceFormat::B8G8R8A8);
1372 if (!dt) {
1373 return nullptr;
1374 }
1376 RefPtr<SourceSurface> source = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, surf);
1378 Matrix m;
1379 m.Translate(0.0, mHeight);
1380 m.Scale(1.0, -1.0);
1381 dt->SetTransform(m);
1383 dt->DrawSurface(source,
1384 Rect(0, 0, mWidth, mHeight),
1385 Rect(0, 0, mWidth, mHeight),
1386 DrawSurfaceOptions(),
1387 DrawOptions(1.0f, CompositionOp::OP_SOURCE));
1389 return dt->Snapshot();
1390 }
1392 //
1393 // XPCOM goop
1394 //
1396 NS_IMPL_CYCLE_COLLECTING_ADDREF(WebGLContext)
1397 NS_IMPL_CYCLE_COLLECTING_RELEASE(WebGLContext)
1399 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_13(WebGLContext,
1400 mCanvasElement,
1401 mExtensions,
1402 mBound2DTextures,
1403 mBoundCubeMapTextures,
1404 mBoundArrayBuffer,
1405 mBoundTransformFeedbackBuffer,
1406 mCurrentProgram,
1407 mBoundFramebuffer,
1408 mBoundRenderbuffer,
1409 mBoundVertexArray,
1410 mDefaultVertexArray,
1411 mActiveOcclusionQuery,
1412 mActiveTransformFeedbackQuery)
1414 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebGLContext)
1415 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
1416 NS_INTERFACE_MAP_ENTRY(nsIDOMWebGLRenderingContext)
1417 NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal)
1418 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
1419 // If the exact way we cast to nsISupports here ever changes, fix our
1420 // ToSupports() method.
1421 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMWebGLRenderingContext)
1422 NS_INTERFACE_MAP_END