1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/canvas/src/WebGLContext.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1422 @@ 1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "WebGLContext.h" 1.10 +#include "WebGL1Context.h" 1.11 +#include "WebGLObjectModel.h" 1.12 +#include "WebGLExtensions.h" 1.13 +#include "WebGLContextUtils.h" 1.14 +#include "WebGLBuffer.h" 1.15 +#include "WebGLVertexAttribData.h" 1.16 +#include "WebGLMemoryTracker.h" 1.17 +#include "WebGLFramebuffer.h" 1.18 +#include "WebGLVertexArray.h" 1.19 +#include "WebGLQuery.h" 1.20 + 1.21 +#include "AccessCheck.h" 1.22 +#include "nsIConsoleService.h" 1.23 +#include "nsServiceManagerUtils.h" 1.24 +#include "nsIClassInfoImpl.h" 1.25 +#include "nsContentUtils.h" 1.26 +#include "nsIXPConnect.h" 1.27 +#include "nsError.h" 1.28 +#include "nsIGfxInfo.h" 1.29 +#include "nsIWidget.h" 1.30 + 1.31 +#include "nsIVariant.h" 1.32 + 1.33 +#include "ImageEncoder.h" 1.34 + 1.35 +#include "gfxContext.h" 1.36 +#include "gfxPattern.h" 1.37 +#include "gfxUtils.h" 1.38 + 1.39 +#include "CanvasUtils.h" 1.40 +#include "nsDisplayList.h" 1.41 + 1.42 +#include "GLContextProvider.h" 1.43 +#include "GLContext.h" 1.44 +#include "ScopedGLHelpers.h" 1.45 +#include "GLReadTexImageHelper.h" 1.46 + 1.47 +#include "gfxCrashReporterUtils.h" 1.48 + 1.49 +#include "nsSVGEffects.h" 1.50 + 1.51 +#include "prenv.h" 1.52 + 1.53 +#include "mozilla/Preferences.h" 1.54 +#include "mozilla/Services.h" 1.55 +#include "mozilla/Telemetry.h" 1.56 + 1.57 +#include "nsIObserverService.h" 1.58 +#include "mozilla/Services.h" 1.59 +#include "mozilla/dom/WebGLRenderingContextBinding.h" 1.60 +#include "mozilla/dom/BindingUtils.h" 1.61 +#include "mozilla/dom/ImageData.h" 1.62 +#include "mozilla/ProcessPriorityManager.h" 1.63 +#include "mozilla/EnumeratedArrayCycleCollection.h" 1.64 + 1.65 +#include "Layers.h" 1.66 + 1.67 +#ifdef MOZ_WIDGET_GONK 1.68 +#include "mozilla/layers/ShadowLayers.h" 1.69 +#endif 1.70 + 1.71 +using namespace mozilla; 1.72 +using namespace mozilla::dom; 1.73 +using namespace mozilla::gfx; 1.74 +using namespace mozilla::gl; 1.75 +using namespace mozilla::layers; 1.76 + 1.77 +NS_IMETHODIMP 1.78 +WebGLMemoryPressureObserver::Observe(nsISupports* aSubject, 1.79 + const char* aTopic, 1.80 + const char16_t* aSomeData) 1.81 +{ 1.82 + if (strcmp(aTopic, "memory-pressure")) 1.83 + return NS_OK; 1.84 + 1.85 + bool wantToLoseContext = true; 1.86 + 1.87 + if (!mContext->mCanLoseContextInForeground && 1.88 + ProcessPriorityManager::CurrentProcessIsForeground()) 1.89 + wantToLoseContext = false; 1.90 + else if (!nsCRT::strcmp(aSomeData, 1.91 + MOZ_UTF16("heap-minimize"))) 1.92 + wantToLoseContext = mContext->mLoseContextOnHeapMinimize; 1.93 + 1.94 + if (wantToLoseContext) 1.95 + mContext->ForceLoseContext(); 1.96 + 1.97 + return NS_OK; 1.98 +} 1.99 + 1.100 + 1.101 +WebGLContextOptions::WebGLContextOptions() 1.102 + : alpha(true), depth(true), stencil(false), 1.103 + premultipliedAlpha(true), antialias(true), 1.104 + preserveDrawingBuffer(false) 1.105 +{ 1.106 + // Set default alpha state based on preference. 1.107 + if (Preferences::GetBool("webgl.default-no-alpha", false)) 1.108 + alpha = false; 1.109 +} 1.110 + 1.111 +WebGLContext::WebGLContext() 1.112 + : gl(nullptr) 1.113 +{ 1.114 + SetIsDOMBinding(); 1.115 + 1.116 + mGeneration = 0; 1.117 + mInvalidated = false; 1.118 + mShouldPresent = true; 1.119 + mResetLayer = true; 1.120 + mOptionsFrozen = false; 1.121 + 1.122 + mActiveTexture = 0; 1.123 + mPixelStoreFlipY = false; 1.124 + mPixelStorePremultiplyAlpha = false; 1.125 + mPixelStoreColorspaceConversion = BROWSER_DEFAULT_WEBGL; 1.126 + 1.127 + mShaderValidation = true; 1.128 + 1.129 + mFakeBlackStatus = WebGLContextFakeBlackStatus::NotNeeded; 1.130 + 1.131 + mVertexAttrib0Vector[0] = 0; 1.132 + mVertexAttrib0Vector[1] = 0; 1.133 + mVertexAttrib0Vector[2] = 0; 1.134 + mVertexAttrib0Vector[3] = 1; 1.135 + mFakeVertexAttrib0BufferObjectVector[0] = 0; 1.136 + mFakeVertexAttrib0BufferObjectVector[1] = 0; 1.137 + mFakeVertexAttrib0BufferObjectVector[2] = 0; 1.138 + mFakeVertexAttrib0BufferObjectVector[3] = 1; 1.139 + mFakeVertexAttrib0BufferObjectSize = 0; 1.140 + mFakeVertexAttrib0BufferObject = 0; 1.141 + mFakeVertexAttrib0BufferStatus = WebGLVertexAttrib0Status::Default; 1.142 + 1.143 + mViewportX = 0; 1.144 + mViewportY = 0; 1.145 + mViewportWidth = 0; 1.146 + mViewportHeight = 0; 1.147 + 1.148 + mScissorTestEnabled = 0; 1.149 + mDitherEnabled = 1; 1.150 + mRasterizerDiscardEnabled = 0; // OpenGL ES 3.0 spec p244 1.151 + 1.152 + // initialize some GL values: we're going to get them from the GL and use them as the sizes of arrays, 1.153 + // so in case glGetIntegerv leaves them uninitialized because of a GL bug, we would have very weird crashes. 1.154 + mGLMaxVertexAttribs = 0; 1.155 + mGLMaxTextureUnits = 0; 1.156 + mGLMaxTextureSize = 0; 1.157 + mGLMaxCubeMapTextureSize = 0; 1.158 + mGLMaxRenderbufferSize = 0; 1.159 + mGLMaxTextureImageUnits = 0; 1.160 + mGLMaxVertexTextureImageUnits = 0; 1.161 + mGLMaxVaryingVectors = 0; 1.162 + mGLMaxFragmentUniformVectors = 0; 1.163 + mGLMaxVertexUniformVectors = 0; 1.164 + mGLMaxColorAttachments = 1; 1.165 + mGLMaxDrawBuffers = 1; 1.166 + mGLMaxTransformFeedbackSeparateAttribs = 0; 1.167 + 1.168 + // See OpenGL ES 2.0.25 spec, 6.2 State Tables, table 6.13 1.169 + mPixelStorePackAlignment = 4; 1.170 + mPixelStoreUnpackAlignment = 4; 1.171 + 1.172 + WebGLMemoryTracker::AddWebGLContext(this); 1.173 + 1.174 + mAllowRestore = true; 1.175 + mContextLossTimerRunning = false; 1.176 + mDrawSinceContextLossTimerSet = false; 1.177 + mContextRestorer = do_CreateInstance("@mozilla.org/timer;1"); 1.178 + mContextStatus = ContextNotLost; 1.179 + mContextLostErrorSet = false; 1.180 + mLoseContextOnHeapMinimize = false; 1.181 + mCanLoseContextInForeground = true; 1.182 + 1.183 + mAlreadyGeneratedWarnings = 0; 1.184 + mAlreadyWarnedAboutFakeVertexAttrib0 = false; 1.185 + mAlreadyWarnedAboutViewportLargerThanDest = false; 1.186 + mMaxWarnings = Preferences::GetInt("webgl.max-warnings-per-context", 32); 1.187 + if (mMaxWarnings < -1) 1.188 + { 1.189 + GenerateWarning("webgl.max-warnings-per-context size is too large (seems like a negative value wrapped)"); 1.190 + mMaxWarnings = 0; 1.191 + } 1.192 + 1.193 + mLastUseIndex = 0; 1.194 + 1.195 + InvalidateBufferFetching(); 1.196 + 1.197 + mBackbufferNeedsClear = true; 1.198 + 1.199 + mDisableFragHighP = false; 1.200 + 1.201 + mDrawCallsSinceLastFlush = 0; 1.202 +} 1.203 + 1.204 +WebGLContext::~WebGLContext() 1.205 +{ 1.206 + DestroyResourcesAndContext(); 1.207 + WebGLMemoryTracker::RemoveWebGLContext(this); 1.208 + TerminateContextLossTimer(); 1.209 + mContextRestorer = nullptr; 1.210 +} 1.211 + 1.212 +void 1.213 +WebGLContext::DestroyResourcesAndContext() 1.214 +{ 1.215 + if (mMemoryPressureObserver) { 1.216 + nsCOMPtr<nsIObserverService> observerService 1.217 + = mozilla::services::GetObserverService(); 1.218 + if (observerService) { 1.219 + observerService->RemoveObserver(mMemoryPressureObserver, 1.220 + "memory-pressure"); 1.221 + } 1.222 + mMemoryPressureObserver = nullptr; 1.223 + } 1.224 + 1.225 + if (!gl) 1.226 + return; 1.227 + 1.228 + gl->MakeCurrent(); 1.229 + 1.230 + mBound2DTextures.Clear(); 1.231 + mBoundCubeMapTextures.Clear(); 1.232 + mBoundArrayBuffer = nullptr; 1.233 + mBoundTransformFeedbackBuffer = nullptr; 1.234 + mCurrentProgram = nullptr; 1.235 + mBoundFramebuffer = nullptr; 1.236 + mActiveOcclusionQuery = nullptr; 1.237 + mBoundRenderbuffer = nullptr; 1.238 + mBoundVertexArray = nullptr; 1.239 + mDefaultVertexArray = nullptr; 1.240 + 1.241 + while (!mTextures.isEmpty()) 1.242 + mTextures.getLast()->DeleteOnce(); 1.243 + while (!mVertexArrays.isEmpty()) 1.244 + mVertexArrays.getLast()->DeleteOnce(); 1.245 + while (!mBuffers.isEmpty()) 1.246 + mBuffers.getLast()->DeleteOnce(); 1.247 + while (!mRenderbuffers.isEmpty()) 1.248 + mRenderbuffers.getLast()->DeleteOnce(); 1.249 + while (!mFramebuffers.isEmpty()) 1.250 + mFramebuffers.getLast()->DeleteOnce(); 1.251 + while (!mShaders.isEmpty()) 1.252 + mShaders.getLast()->DeleteOnce(); 1.253 + while (!mPrograms.isEmpty()) 1.254 + mPrograms.getLast()->DeleteOnce(); 1.255 + while (!mQueries.isEmpty()) 1.256 + mQueries.getLast()->DeleteOnce(); 1.257 + 1.258 + mBlackOpaqueTexture2D = nullptr; 1.259 + mBlackOpaqueTextureCubeMap = nullptr; 1.260 + mBlackTransparentTexture2D = nullptr; 1.261 + mBlackTransparentTextureCubeMap = nullptr; 1.262 + 1.263 + if (mFakeVertexAttrib0BufferObject) { 1.264 + gl->fDeleteBuffers(1, &mFakeVertexAttrib0BufferObject); 1.265 + } 1.266 + 1.267 + // disable all extensions except "WEBGL_lose_context". see bug #927969 1.268 + // spec: http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2 1.269 + for (size_t i = 0; i < size_t(WebGLExtensionID::Max); ++i) { 1.270 + WebGLExtensionID extension = WebGLExtensionID(i); 1.271 + 1.272 + if (!IsExtensionEnabled(extension) || (extension == WebGLExtensionID::WEBGL_lose_context)) 1.273 + continue; 1.274 + 1.275 + mExtensions[extension]->MarkLost(); 1.276 + mExtensions[extension] = nullptr; 1.277 + } 1.278 + 1.279 + // We just got rid of everything, so the context had better 1.280 + // have been going away. 1.281 +#ifdef DEBUG 1.282 + if (gl->DebugMode()) { 1.283 + printf_stderr("--- WebGL context destroyed: %p\n", gl.get()); 1.284 + } 1.285 +#endif 1.286 + 1.287 + gl = nullptr; 1.288 +} 1.289 + 1.290 +void 1.291 +WebGLContext::Invalidate() 1.292 +{ 1.293 + if (mInvalidated) 1.294 + return; 1.295 + 1.296 + if (!mCanvasElement) 1.297 + return; 1.298 + 1.299 + nsSVGEffects::InvalidateDirectRenderingObservers(mCanvasElement); 1.300 + 1.301 + mInvalidated = true; 1.302 + mCanvasElement->InvalidateCanvasContent(nullptr); 1.303 +} 1.304 + 1.305 +// 1.306 +// nsICanvasRenderingContextInternal 1.307 +// 1.308 + 1.309 +NS_IMETHODIMP 1.310 +WebGLContext::SetContextOptions(JSContext* aCx, JS::Handle<JS::Value> aOptions) 1.311 +{ 1.312 + if (aOptions.isNullOrUndefined() && mOptionsFrozen) { 1.313 + return NS_OK; 1.314 + } 1.315 + 1.316 + WebGLContextAttributes attributes; 1.317 + NS_ENSURE_TRUE(attributes.Init(aCx, aOptions), NS_ERROR_UNEXPECTED); 1.318 + 1.319 + WebGLContextOptions newOpts; 1.320 + 1.321 + newOpts.stencil = attributes.mStencil; 1.322 + newOpts.depth = attributes.mDepth; 1.323 + newOpts.premultipliedAlpha = attributes.mPremultipliedAlpha; 1.324 + newOpts.antialias = attributes.mAntialias; 1.325 + newOpts.preserveDrawingBuffer = attributes.mPreserveDrawingBuffer; 1.326 + if (attributes.mAlpha.WasPassed()) { 1.327 + newOpts.alpha = attributes.mAlpha.Value(); 1.328 + } 1.329 + 1.330 + // enforce that if stencil is specified, we also give back depth 1.331 + newOpts.depth |= newOpts.stencil; 1.332 + 1.333 +#if 0 1.334 + GenerateWarning("aaHint: %d stencil: %d depth: %d alpha: %d premult: %d preserve: %d\n", 1.335 + newOpts.antialias ? 1 : 0, 1.336 + newOpts.stencil ? 1 : 0, 1.337 + newOpts.depth ? 1 : 0, 1.338 + newOpts.alpha ? 1 : 0, 1.339 + newOpts.premultipliedAlpha ? 1 : 0, 1.340 + newOpts.preserveDrawingBuffer ? 1 : 0); 1.341 +#endif 1.342 + 1.343 + if (mOptionsFrozen && newOpts != mOptions) { 1.344 + // Error if the options are already frozen, and the ones that were asked for 1.345 + // aren't the same as what they were originally. 1.346 + return NS_ERROR_FAILURE; 1.347 + } 1.348 + 1.349 + mOptions = newOpts; 1.350 + return NS_OK; 1.351 +} 1.352 + 1.353 +#ifdef DEBUG 1.354 +int32_t 1.355 +WebGLContext::GetWidth() const 1.356 +{ 1.357 + return mWidth; 1.358 +} 1.359 + 1.360 +int32_t 1.361 +WebGLContext::GetHeight() const 1.362 +{ 1.363 + return mHeight; 1.364 +} 1.365 +#endif 1.366 + 1.367 +NS_IMETHODIMP 1.368 +WebGLContext::SetDimensions(int32_t width, int32_t height) 1.369 +{ 1.370 + // Early error return cases 1.371 + 1.372 + if (width < 0 || height < 0) { 1.373 + GenerateWarning("Canvas size is too large (seems like a negative value wrapped)"); 1.374 + return NS_ERROR_OUT_OF_MEMORY; 1.375 + } 1.376 + 1.377 + if (!GetCanvas()) 1.378 + return NS_ERROR_FAILURE; 1.379 + 1.380 + // Early success return cases 1.381 + 1.382 + GetCanvas()->InvalidateCanvas(); 1.383 + 1.384 + if (gl && mWidth == width && mHeight == height) 1.385 + return NS_OK; 1.386 + 1.387 + // Zero-sized surfaces can cause problems. 1.388 + if (width == 0) { 1.389 + width = 1; 1.390 + } 1.391 + if (height == 0) { 1.392 + height = 1; 1.393 + } 1.394 + 1.395 + // If we already have a gl context, then we just need to resize it 1.396 + if (gl) { 1.397 + MakeContextCurrent(); 1.398 + 1.399 + // If we've already drawn, we should commit the current buffer. 1.400 + PresentScreenBuffer(); 1.401 + 1.402 + // ResizeOffscreen scraps the current prod buffer before making a new one. 1.403 + gl->ResizeOffscreen(gfx::IntSize(width, height)); // Doesn't matter if it succeeds (soft-fail) 1.404 + // It's unlikely that we'll get a proper-sized context if we recreate if we didn't on resize 1.405 + 1.406 + // everything's good, we're done here 1.407 + mWidth = gl->OffscreenSize().width; 1.408 + mHeight = gl->OffscreenSize().height; 1.409 + mResetLayer = true; 1.410 + 1.411 + mBackbufferNeedsClear = true; 1.412 + 1.413 + return NS_OK; 1.414 + } 1.415 + 1.416 + // End of early return cases. 1.417 + // At this point we know that we're not just resizing an existing context, 1.418 + // we are initializing a new context. 1.419 + 1.420 + // if we exceeded either the global or the per-principal limit for WebGL contexts, 1.421 + // lose the oldest-used context now to free resources. Note that we can't do that 1.422 + // in the WebGLContext constructor as we don't have a canvas element yet there. 1.423 + // Here is the right place to do so, as we are about to create the OpenGL context 1.424 + // and that is what can fail if we already have too many. 1.425 + LoseOldestWebGLContextIfLimitExceeded(); 1.426 + 1.427 + // Get some prefs for some preferred/overriden things 1.428 + NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_FAILURE); 1.429 + 1.430 +#ifdef XP_WIN 1.431 + bool preferEGL = 1.432 + Preferences::GetBool("webgl.prefer-egl", false); 1.433 + bool preferOpenGL = 1.434 + Preferences::GetBool("webgl.prefer-native-gl", false); 1.435 +#endif 1.436 + bool forceEnabled = 1.437 + Preferences::GetBool("webgl.force-enabled", false); 1.438 + bool disabled = 1.439 + Preferences::GetBool("webgl.disabled", false); 1.440 + bool prefer16bit = 1.441 + Preferences::GetBool("webgl.prefer-16bpp", false); 1.442 + 1.443 + ScopedGfxFeatureReporter reporter("WebGL", forceEnabled); 1.444 + 1.445 + if (disabled) 1.446 + return NS_ERROR_FAILURE; 1.447 + 1.448 + // We're going to create an entirely new context. If our 1.449 + // generation is not 0 right now (that is, if this isn't the first 1.450 + // context we're creating), we may have to dispatch a context lost 1.451 + // event. 1.452 + 1.453 + // If incrementing the generation would cause overflow, 1.454 + // don't allow it. Allowing this would allow us to use 1.455 + // resource handles created from older context generations. 1.456 + if (!(mGeneration + 1).isValid()) 1.457 + return NS_ERROR_FAILURE; // exit without changing the value of mGeneration 1.458 + 1.459 + SurfaceCaps caps; 1.460 + 1.461 + caps.color = true; 1.462 + caps.alpha = mOptions.alpha; 1.463 + caps.depth = mOptions.depth; 1.464 + caps.stencil = mOptions.stencil; 1.465 + 1.466 + // we should really have this behind a 1.467 + // |gfxPlatform::GetPlatform()->GetScreenDepth() == 16| check, but 1.468 + // for now it's just behind a pref for testing/evaluation. 1.469 + caps.bpp16 = prefer16bit; 1.470 + 1.471 + caps.preserve = mOptions.preserveDrawingBuffer; 1.472 + 1.473 +#ifdef MOZ_WIDGET_GONK 1.474 + nsIWidget *docWidget = nsContentUtils::WidgetForDocument(mCanvasElement->OwnerDoc()); 1.475 + if (docWidget) { 1.476 + layers::LayerManager *layerManager = docWidget->GetLayerManager(); 1.477 + if (layerManager) { 1.478 + // XXX we really want "AsSurfaceAllocator" here for generality 1.479 + layers::ShadowLayerForwarder *forwarder = layerManager->AsShadowForwarder(); 1.480 + if (forwarder) { 1.481 + caps.surfaceAllocator = static_cast<layers::ISurfaceAllocator*>(forwarder); 1.482 + } 1.483 + } 1.484 + } 1.485 +#endif 1.486 + 1.487 + bool forceMSAA = 1.488 + Preferences::GetBool("webgl.msaa-force", false); 1.489 + 1.490 + int32_t status; 1.491 + nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1"); 1.492 + if (mOptions.antialias && 1.493 + gfxInfo && 1.494 + NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBGL_MSAA, &status))) { 1.495 + if (status == nsIGfxInfo::FEATURE_NO_INFO || forceMSAA) { 1.496 + caps.antialias = true; 1.497 + } 1.498 + } 1.499 + 1.500 +#ifdef XP_WIN 1.501 + if (PR_GetEnv("MOZ_WEBGL_PREFER_EGL")) { 1.502 + preferEGL = true; 1.503 + } 1.504 +#endif 1.505 + 1.506 + // Ask GfxInfo about what we should use 1.507 + bool useOpenGL = true; 1.508 + 1.509 +#ifdef XP_WIN 1.510 + bool useANGLE = true; 1.511 +#endif 1.512 + 1.513 + if (gfxInfo && !forceEnabled) { 1.514 + if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBGL_OPENGL, &status))) { 1.515 + if (status != nsIGfxInfo::FEATURE_NO_INFO) { 1.516 + useOpenGL = false; 1.517 + } 1.518 + } 1.519 +#ifdef XP_WIN 1.520 + if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBGL_ANGLE, &status))) { 1.521 + if (status != nsIGfxInfo::FEATURE_NO_INFO) { 1.522 + useANGLE = false; 1.523 + } 1.524 + } 1.525 +#endif 1.526 + } 1.527 + 1.528 +#ifdef XP_WIN 1.529 + // allow forcing GL and not EGL/ANGLE 1.530 + if (PR_GetEnv("MOZ_WEBGL_FORCE_OPENGL")) { 1.531 + preferEGL = false; 1.532 + useANGLE = false; 1.533 + useOpenGL = true; 1.534 + } 1.535 +#endif 1.536 + 1.537 + gfxIntSize size(width, height); 1.538 + 1.539 +#ifdef XP_WIN 1.540 + // if we want EGL, try it now 1.541 + if (!gl && (preferEGL || useANGLE) && !preferOpenGL) { 1.542 + gl = gl::GLContextProviderEGL::CreateOffscreen(size, caps); 1.543 + if (!gl || !InitAndValidateGL()) { 1.544 + GenerateWarning("Error during ANGLE OpenGL ES initialization"); 1.545 + return NS_ERROR_FAILURE; 1.546 + } 1.547 + } 1.548 +#endif 1.549 + 1.550 + // try the default provider, whatever that is 1.551 + if (!gl && useOpenGL) { 1.552 + gl = gl::GLContextProvider::CreateOffscreen(size, caps); 1.553 + if (gl && !InitAndValidateGL()) { 1.554 + GenerateWarning("Error during OpenGL initialization"); 1.555 + return NS_ERROR_FAILURE; 1.556 + } 1.557 + } 1.558 + 1.559 + if (!gl) { 1.560 + GenerateWarning("Can't get a usable WebGL context"); 1.561 + return NS_ERROR_FAILURE; 1.562 + } 1.563 + 1.564 +#ifdef DEBUG 1.565 + if (gl->DebugMode()) { 1.566 + printf_stderr("--- WebGL context created: %p\n", gl.get()); 1.567 + } 1.568 +#endif 1.569 + 1.570 + mWidth = width; 1.571 + mHeight = height; 1.572 + mViewportWidth = width; 1.573 + mViewportHeight = height; 1.574 + mResetLayer = true; 1.575 + mOptionsFrozen = true; 1.576 + 1.577 + mHasRobustness = gl->HasRobustness(); 1.578 + 1.579 + // increment the generation number 1.580 + ++mGeneration; 1.581 + 1.582 +#if 0 1.583 + if (mGeneration > 0) { 1.584 + // XXX dispatch context lost event 1.585 + } 1.586 +#endif 1.587 + 1.588 + MakeContextCurrent(); 1.589 + 1.590 + // Make sure that we clear this out, otherwise 1.591 + // we'll end up displaying random memory 1.592 + gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0); 1.593 + 1.594 + gl->fClearColor(0.0f, 0.0f, 0.0f, 0.0f); 1.595 + gl->fClearDepth(1.0f); 1.596 + gl->fClearStencil(0); 1.597 + 1.598 + mBackbufferNeedsClear = true; 1.599 + 1.600 + // Clear immediately, because we need to present the cleared initial 1.601 + // buffer. 1.602 + ClearBackbufferIfNeeded(); 1.603 + 1.604 + mShouldPresent = true; 1.605 + 1.606 + MOZ_ASSERT(gl->Caps().color == caps.color); 1.607 + MOZ_ASSERT(gl->Caps().alpha == caps.alpha); 1.608 + MOZ_ASSERT(gl->Caps().depth == caps.depth || !gl->Caps().depth); 1.609 + MOZ_ASSERT(gl->Caps().stencil == caps.stencil || !gl->Caps().stencil); 1.610 + MOZ_ASSERT(gl->Caps().antialias == caps.antialias || !gl->Caps().antialias); 1.611 + MOZ_ASSERT(gl->Caps().preserve == caps.preserve); 1.612 + 1.613 + reporter.SetSuccessful(); 1.614 + return NS_OK; 1.615 +} 1.616 + 1.617 +void 1.618 +WebGLContext::ClearBackbufferIfNeeded() 1.619 +{ 1.620 + if (!mBackbufferNeedsClear) 1.621 + return; 1.622 + 1.623 +#ifdef DEBUG 1.624 + gl->MakeCurrent(); 1.625 + 1.626 + GLuint fb = 0; 1.627 + gl->GetUIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, &fb); 1.628 + MOZ_ASSERT(fb == 0); 1.629 +#endif 1.630 + 1.631 + ClearScreen(); 1.632 + 1.633 + mBackbufferNeedsClear = false; 1.634 +} 1.635 + 1.636 +void WebGLContext::LoseOldestWebGLContextIfLimitExceeded() 1.637 +{ 1.638 +#ifdef MOZ_GFX_OPTIMIZE_MOBILE 1.639 + // some mobile devices can't have more than 8 GL contexts overall 1.640 + const size_t kMaxWebGLContextsPerPrincipal = 2; 1.641 + const size_t kMaxWebGLContexts = 4; 1.642 +#else 1.643 + const size_t kMaxWebGLContextsPerPrincipal = 16; 1.644 + const size_t kMaxWebGLContexts = 32; 1.645 +#endif 1.646 + MOZ_ASSERT(kMaxWebGLContextsPerPrincipal < kMaxWebGLContexts); 1.647 + 1.648 + // it's important to update the index on a new context before losing old contexts, 1.649 + // otherwise new unused contexts would all have index 0 and we couldn't distinguish older ones 1.650 + // when choosing which one to lose first. 1.651 + UpdateLastUseIndex(); 1.652 + 1.653 + WebGLMemoryTracker::ContextsArrayType &contexts 1.654 + = WebGLMemoryTracker::Contexts(); 1.655 + 1.656 + // quick exit path, should cover a majority of cases 1.657 + if (contexts.Length() <= kMaxWebGLContextsPerPrincipal) { 1.658 + return; 1.659 + } 1.660 + 1.661 + // note that here by "context" we mean "non-lost context". See the check for 1.662 + // IsContextLost() below. Indeed, the point of this function is to maybe lose 1.663 + // some currently non-lost context. 1.664 + 1.665 + uint64_t oldestIndex = UINT64_MAX; 1.666 + uint64_t oldestIndexThisPrincipal = UINT64_MAX; 1.667 + const WebGLContext *oldestContext = nullptr; 1.668 + const WebGLContext *oldestContextThisPrincipal = nullptr; 1.669 + size_t numContexts = 0; 1.670 + size_t numContextsThisPrincipal = 0; 1.671 + 1.672 + for(size_t i = 0; i < contexts.Length(); ++i) { 1.673 + 1.674 + // don't want to lose ourselves. 1.675 + if (contexts[i] == this) 1.676 + continue; 1.677 + 1.678 + if (contexts[i]->IsContextLost()) 1.679 + continue; 1.680 + 1.681 + if (!contexts[i]->GetCanvas()) { 1.682 + // Zombie context: the canvas is already destroyed, but something else 1.683 + // (typically the compositor) is still holding on to the context. 1.684 + // Killing zombies is a no-brainer. 1.685 + const_cast<WebGLContext*>(contexts[i])->LoseContext(); 1.686 + continue; 1.687 + } 1.688 + 1.689 + numContexts++; 1.690 + if (contexts[i]->mLastUseIndex < oldestIndex) { 1.691 + oldestIndex = contexts[i]->mLastUseIndex; 1.692 + oldestContext = contexts[i]; 1.693 + } 1.694 + 1.695 + nsIPrincipal *ourPrincipal = GetCanvas()->NodePrincipal(); 1.696 + nsIPrincipal *theirPrincipal = contexts[i]->GetCanvas()->NodePrincipal(); 1.697 + bool samePrincipal; 1.698 + nsresult rv = ourPrincipal->Equals(theirPrincipal, &samePrincipal); 1.699 + if (NS_SUCCEEDED(rv) && samePrincipal) { 1.700 + numContextsThisPrincipal++; 1.701 + if (contexts[i]->mLastUseIndex < oldestIndexThisPrincipal) { 1.702 + oldestIndexThisPrincipal = contexts[i]->mLastUseIndex; 1.703 + oldestContextThisPrincipal = contexts[i]; 1.704 + } 1.705 + } 1.706 + } 1.707 + 1.708 + if (numContextsThisPrincipal > kMaxWebGLContextsPerPrincipal) { 1.709 + GenerateWarning("Exceeded %d live WebGL contexts for this principal, losing the " 1.710 + "least recently used one.", kMaxWebGLContextsPerPrincipal); 1.711 + MOZ_ASSERT(oldestContextThisPrincipal); // if we reach this point, this can't be null 1.712 + const_cast<WebGLContext*>(oldestContextThisPrincipal)->LoseContext(); 1.713 + } else if (numContexts > kMaxWebGLContexts) { 1.714 + GenerateWarning("Exceeded %d live WebGL contexts, losing the least recently used one.", 1.715 + kMaxWebGLContexts); 1.716 + MOZ_ASSERT(oldestContext); // if we reach this point, this can't be null 1.717 + const_cast<WebGLContext*>(oldestContext)->LoseContext(); 1.718 + } 1.719 +} 1.720 + 1.721 +void 1.722 +WebGLContext::GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat) 1.723 +{ 1.724 + *aImageBuffer = nullptr; 1.725 + *aFormat = 0; 1.726 + 1.727 + // Use GetSurfaceSnapshot() to make sure that appropriate y-flip gets applied 1.728 + bool premult; 1.729 + RefPtr<SourceSurface> snapshot = 1.730 + GetSurfaceSnapshot(mOptions.premultipliedAlpha ? nullptr : &premult); 1.731 + if (!snapshot) { 1.732 + return; 1.733 + } 1.734 + MOZ_ASSERT(mOptions.premultipliedAlpha || !premult, "We must get unpremult when we ask for it!"); 1.735 + 1.736 + RefPtr<DataSourceSurface> dataSurface = snapshot->GetDataSurface(); 1.737 + 1.738 + DataSourceSurface::MappedSurface map; 1.739 + if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) { 1.740 + return; 1.741 + } 1.742 + 1.743 + static const fallible_t fallible = fallible_t(); 1.744 + uint8_t* imageBuffer = new (fallible) uint8_t[mWidth * mHeight * 4]; 1.745 + if (!imageBuffer) { 1.746 + dataSurface->Unmap(); 1.747 + return; 1.748 + } 1.749 + memcpy(imageBuffer, map.mData, mWidth * mHeight * 4); 1.750 + 1.751 + dataSurface->Unmap(); 1.752 + 1.753 + int32_t format = imgIEncoder::INPUT_FORMAT_HOSTARGB; 1.754 + if (!mOptions.premultipliedAlpha) { 1.755 + // We need to convert to INPUT_FORMAT_RGBA, otherwise 1.756 + // we are automatically considered premult, and unpremult'd. 1.757 + // Yes, it is THAT silly. 1.758 + // Except for different lossy conversions by color, 1.759 + // we could probably just change the label, and not change the data. 1.760 + gfxUtils::ConvertBGRAtoRGBA(imageBuffer, mWidth * mHeight * 4); 1.761 + format = imgIEncoder::INPUT_FORMAT_RGBA; 1.762 + } 1.763 + 1.764 + *aImageBuffer = imageBuffer; 1.765 + *aFormat = format; 1.766 +} 1.767 + 1.768 +NS_IMETHODIMP 1.769 +WebGLContext::GetInputStream(const char* aMimeType, 1.770 + const char16_t* aEncoderOptions, 1.771 + nsIInputStream **aStream) 1.772 +{ 1.773 + NS_ASSERTION(gl, "GetInputStream on invalid context?"); 1.774 + if (!gl) 1.775 + return NS_ERROR_FAILURE; 1.776 + 1.777 + nsCString enccid("@mozilla.org/image/encoder;2?type="); 1.778 + enccid += aMimeType; 1.779 + nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get()); 1.780 + if (!encoder) { 1.781 + return NS_ERROR_FAILURE; 1.782 + } 1.783 + 1.784 + nsAutoArrayPtr<uint8_t> imageBuffer; 1.785 + int32_t format = 0; 1.786 + GetImageBuffer(getter_Transfers(imageBuffer), &format); 1.787 + if (!imageBuffer) { 1.788 + return NS_ERROR_FAILURE; 1.789 + } 1.790 + 1.791 + return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer, format, 1.792 + encoder, aEncoderOptions, aStream); 1.793 +} 1.794 + 1.795 +void WebGLContext::UpdateLastUseIndex() 1.796 +{ 1.797 + static CheckedInt<uint64_t> sIndex = 0; 1.798 + 1.799 + sIndex++; 1.800 + 1.801 + // should never happen with 64-bit; trying to handle this would be riskier than 1.802 + // not handling it as the handler code would never get exercised. 1.803 + if (!sIndex.isValid()) { 1.804 + NS_RUNTIMEABORT("Can't believe it's been 2^64 transactions already!"); 1.805 + } 1.806 + 1.807 + mLastUseIndex = sIndex.value(); 1.808 +} 1.809 + 1.810 +static uint8_t gWebGLLayerUserData; 1.811 + 1.812 +namespace mozilla { 1.813 + 1.814 +class WebGLContextUserData : public LayerUserData { 1.815 +public: 1.816 + WebGLContextUserData(HTMLCanvasElement *aContent) 1.817 + : mContent(aContent) 1.818 + {} 1.819 + 1.820 + /* PreTransactionCallback gets called by the Layers code every time the 1.821 + * WebGL canvas is going to be composited. 1.822 + */ 1.823 + static void PreTransactionCallback(void* data) 1.824 + { 1.825 + WebGLContextUserData* userdata = static_cast<WebGLContextUserData*>(data); 1.826 + HTMLCanvasElement* canvas = userdata->mContent; 1.827 + WebGLContext* context = static_cast<WebGLContext*>(canvas->GetContextAtIndex(0)); 1.828 + 1.829 + // Present our screenbuffer, if needed. 1.830 + context->PresentScreenBuffer(); 1.831 + context->mDrawCallsSinceLastFlush = 0; 1.832 + } 1.833 + 1.834 + /** DidTransactionCallback gets called by the Layers code everytime the WebGL canvas gets composite, 1.835 + * so it really is the right place to put actions that have to be performed upon compositing 1.836 + */ 1.837 + static void DidTransactionCallback(void* aData) 1.838 + { 1.839 + WebGLContextUserData *userdata = static_cast<WebGLContextUserData*>(aData); 1.840 + HTMLCanvasElement *canvas = userdata->mContent; 1.841 + WebGLContext *context = static_cast<WebGLContext*>(canvas->GetContextAtIndex(0)); 1.842 + 1.843 + // Mark ourselves as no longer invalidated. 1.844 + context->MarkContextClean(); 1.845 + 1.846 + context->UpdateLastUseIndex(); 1.847 + } 1.848 + 1.849 +private: 1.850 + nsRefPtr<HTMLCanvasElement> mContent; 1.851 +}; 1.852 + 1.853 +} // end namespace mozilla 1.854 + 1.855 +already_AddRefed<layers::CanvasLayer> 1.856 +WebGLContext::GetCanvasLayer(nsDisplayListBuilder* aBuilder, 1.857 + CanvasLayer *aOldLayer, 1.858 + LayerManager *aManager) 1.859 +{ 1.860 + if (IsContextLost()) 1.861 + return nullptr; 1.862 + 1.863 + if (!mResetLayer && aOldLayer && 1.864 + aOldLayer->HasUserData(&gWebGLLayerUserData)) { 1.865 + nsRefPtr<layers::CanvasLayer> ret = aOldLayer; 1.866 + return ret.forget(); 1.867 + } 1.868 + 1.869 + nsRefPtr<CanvasLayer> canvasLayer = aManager->CreateCanvasLayer(); 1.870 + if (!canvasLayer) { 1.871 + NS_WARNING("CreateCanvasLayer returned null!"); 1.872 + return nullptr; 1.873 + } 1.874 + WebGLContextUserData *userData = nullptr; 1.875 + if (aBuilder->IsPaintingToWindow()) { 1.876 + // Make the layer tell us whenever a transaction finishes (including 1.877 + // the current transaction), so we can clear our invalidation state and 1.878 + // start invalidating again. We need to do this for the layer that is 1.879 + // being painted to a window (there shouldn't be more than one at a time, 1.880 + // and if there is, flushing the invalidation state more often than 1.881 + // necessary is harmless). 1.882 + 1.883 + // The layer will be destroyed when we tear down the presentation 1.884 + // (at the latest), at which time this userData will be destroyed, 1.885 + // releasing the reference to the element. 1.886 + // The userData will receive DidTransactionCallbacks, which flush the 1.887 + // the invalidation state to indicate that the canvas is up to date. 1.888 + userData = new WebGLContextUserData(mCanvasElement); 1.889 + canvasLayer->SetDidTransactionCallback( 1.890 + WebGLContextUserData::DidTransactionCallback, userData); 1.891 + canvasLayer->SetPreTransactionCallback( 1.892 + WebGLContextUserData::PreTransactionCallback, userData); 1.893 + } 1.894 + canvasLayer->SetUserData(&gWebGLLayerUserData, userData); 1.895 + 1.896 + CanvasLayer::Data data; 1.897 + data.mGLContext = gl; 1.898 + data.mSize = nsIntSize(mWidth, mHeight); 1.899 + data.mIsGLAlphaPremult = IsPremultAlpha(); 1.900 + 1.901 + canvasLayer->Initialize(data); 1.902 + uint32_t flags = gl->Caps().alpha ? 0 : Layer::CONTENT_OPAQUE; 1.903 + canvasLayer->SetContentFlags(flags); 1.904 + canvasLayer->Updated(); 1.905 + 1.906 + mResetLayer = false; 1.907 + 1.908 + return canvasLayer.forget(); 1.909 +} 1.910 + 1.911 +void 1.912 +WebGLContext::GetContextAttributes(Nullable<dom::WebGLContextAttributes> &retval) 1.913 +{ 1.914 + retval.SetNull(); 1.915 + if (IsContextLost()) 1.916 + return; 1.917 + 1.918 + dom::WebGLContextAttributes& result = retval.SetValue(); 1.919 + 1.920 + const PixelBufferFormat& format = gl->GetPixelFormat(); 1.921 + 1.922 + result.mAlpha.Construct(format.alpha > 0); 1.923 + result.mDepth = format.depth > 0; 1.924 + result.mStencil = format.stencil > 0; 1.925 + result.mAntialias = format.samples > 1; 1.926 + result.mPremultipliedAlpha = mOptions.premultipliedAlpha; 1.927 + result.mPreserveDrawingBuffer = mOptions.preserveDrawingBuffer; 1.928 +} 1.929 + 1.930 +/* [noscript] DOMString mozGetUnderlyingParamString(in GLenum pname); */ 1.931 +NS_IMETHODIMP 1.932 +WebGLContext::MozGetUnderlyingParamString(uint32_t pname, nsAString& retval) 1.933 +{ 1.934 + if (IsContextLost()) 1.935 + return NS_OK; 1.936 + 1.937 + retval.SetIsVoid(true); 1.938 + 1.939 + MakeContextCurrent(); 1.940 + 1.941 + switch (pname) { 1.942 + case LOCAL_GL_VENDOR: 1.943 + case LOCAL_GL_RENDERER: 1.944 + case LOCAL_GL_VERSION: 1.945 + case LOCAL_GL_SHADING_LANGUAGE_VERSION: 1.946 + case LOCAL_GL_EXTENSIONS: { 1.947 + const char *s = (const char *) gl->fGetString(pname); 1.948 + retval.Assign(NS_ConvertASCIItoUTF16(nsDependentCString(s))); 1.949 + } 1.950 + break; 1.951 + 1.952 + default: 1.953 + return NS_ERROR_INVALID_ARG; 1.954 + } 1.955 + 1.956 + return NS_OK; 1.957 +} 1.958 + 1.959 +void 1.960 +WebGLContext::ClearScreen() 1.961 +{ 1.962 + bool colorAttachmentsMask[WebGLContext::sMaxColorAttachments] = {false}; 1.963 + 1.964 + MakeContextCurrent(); 1.965 + ScopedBindFramebuffer autoFB(gl, 0); 1.966 + 1.967 + GLbitfield clearMask = LOCAL_GL_COLOR_BUFFER_BIT; 1.968 + if (mOptions.depth) 1.969 + clearMask |= LOCAL_GL_DEPTH_BUFFER_BIT; 1.970 + if (mOptions.stencil) 1.971 + clearMask |= LOCAL_GL_STENCIL_BUFFER_BIT; 1.972 + 1.973 + colorAttachmentsMask[0] = true; 1.974 + 1.975 + ForceClearFramebufferWithDefaultValues(clearMask, colorAttachmentsMask); 1.976 +} 1.977 + 1.978 +#ifdef DEBUG 1.979 +// For NaNs, etc. 1.980 +static bool IsShadowCorrect(float shadow, float actual) { 1.981 + if (IsNaN(shadow)) { 1.982 + // GL is allowed to do anything it wants for NaNs, so if we're shadowing 1.983 + // a NaN, then whatever `actual` is might be correct. 1.984 + return true; 1.985 + } 1.986 + 1.987 + return shadow == actual; 1.988 +} 1.989 +#endif 1.990 + 1.991 +void 1.992 +WebGLContext::ForceClearFramebufferWithDefaultValues(GLbitfield mask, const bool colorAttachmentsMask[sMaxColorAttachments]) 1.993 +{ 1.994 + MakeContextCurrent(); 1.995 + 1.996 + bool initializeColorBuffer = 0 != (mask & LOCAL_GL_COLOR_BUFFER_BIT); 1.997 + bool initializeDepthBuffer = 0 != (mask & LOCAL_GL_DEPTH_BUFFER_BIT); 1.998 + bool initializeStencilBuffer = 0 != (mask & LOCAL_GL_STENCIL_BUFFER_BIT); 1.999 + bool drawBuffersIsEnabled = IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers); 1.1000 + 1.1001 + GLenum currentDrawBuffers[WebGLContext::sMaxColorAttachments]; 1.1002 + 1.1003 + // Fun GL fact: No need to worry about the viewport here, glViewport is just 1.1004 + // setting up a coordinates transformation, it doesn't affect glClear at all. 1.1005 + 1.1006 +#ifdef DEBUG 1.1007 + // Scope to hide our variables. 1.1008 + { 1.1009 + // Sanity-check that all our state is set properly. Otherwise, when we 1.1010 + // reset out state to what we *think* it is, we'll get it wrong. 1.1011 + 1.1012 + // Dither shouldn't matter when we're clearing to {0,0,0,0}. 1.1013 + MOZ_ASSERT(gl->fIsEnabled(LOCAL_GL_SCISSOR_TEST) == mScissorTestEnabled); 1.1014 + 1.1015 + if (initializeColorBuffer) { 1.1016 + realGLboolean colorWriteMask[4] = {2, 2, 2, 2}; 1.1017 + GLfloat colorClearValue[4] = {-1.0f, -1.0f, -1.0f, -1.0f}; 1.1018 + 1.1019 + gl->fGetBooleanv(LOCAL_GL_COLOR_WRITEMASK, colorWriteMask); 1.1020 + gl->fGetFloatv(LOCAL_GL_COLOR_CLEAR_VALUE, colorClearValue); 1.1021 + 1.1022 + MOZ_ASSERT(colorWriteMask[0] == mColorWriteMask[0] && 1.1023 + colorWriteMask[1] == mColorWriteMask[1] && 1.1024 + colorWriteMask[2] == mColorWriteMask[2] && 1.1025 + colorWriteMask[3] == mColorWriteMask[3]); 1.1026 + MOZ_ASSERT(IsShadowCorrect(mColorClearValue[0], colorClearValue[0]) && 1.1027 + IsShadowCorrect(mColorClearValue[1], colorClearValue[1]) && 1.1028 + IsShadowCorrect(mColorClearValue[2], colorClearValue[2]) && 1.1029 + IsShadowCorrect(mColorClearValue[3], colorClearValue[3])); 1.1030 + } 1.1031 + 1.1032 + if (initializeDepthBuffer) { 1.1033 + realGLboolean depthWriteMask = 2; 1.1034 + GLfloat depthClearValue = -1.0f; 1.1035 + 1.1036 + 1.1037 + gl->fGetBooleanv(LOCAL_GL_DEPTH_WRITEMASK, &depthWriteMask); 1.1038 + gl->fGetFloatv(LOCAL_GL_DEPTH_CLEAR_VALUE, &depthClearValue); 1.1039 + 1.1040 + MOZ_ASSERT(depthWriteMask == mDepthWriteMask); 1.1041 + MOZ_ASSERT(IsShadowCorrect(mDepthClearValue, depthClearValue)); 1.1042 + } 1.1043 + 1.1044 + if (initializeStencilBuffer) { 1.1045 + GLuint stencilWriteMaskFront = 0xdeadbad1; 1.1046 + GLuint stencilWriteMaskBack = 0xdeadbad1; 1.1047 + GLuint stencilClearValue = 0xdeadbad1; 1.1048 + 1.1049 + gl->GetUIntegerv(LOCAL_GL_STENCIL_WRITEMASK, &stencilWriteMaskFront); 1.1050 + gl->GetUIntegerv(LOCAL_GL_STENCIL_BACK_WRITEMASK, &stencilWriteMaskBack); 1.1051 + gl->GetUIntegerv(LOCAL_GL_STENCIL_CLEAR_VALUE, &stencilClearValue); 1.1052 + 1.1053 + GLuint stencilBits = 0; 1.1054 + gl->GetUIntegerv(LOCAL_GL_STENCIL_BITS, &stencilBits); 1.1055 + GLuint stencilMask = (GLuint(1) << stencilBits) - 1; 1.1056 + 1.1057 + MOZ_ASSERT( ( stencilWriteMaskFront & stencilMask) == 1.1058 + (mStencilWriteMaskFront & stencilMask) ); 1.1059 + MOZ_ASSERT( ( stencilWriteMaskBack & stencilMask) == 1.1060 + (mStencilWriteMaskBack & stencilMask) ); 1.1061 + MOZ_ASSERT( ( stencilClearValue & stencilMask) == 1.1062 + (mStencilClearValue & stencilMask) ); 1.1063 + } 1.1064 + } 1.1065 +#endif 1.1066 + 1.1067 + // Prepare GL state for clearing. 1.1068 + gl->fDisable(LOCAL_GL_SCISSOR_TEST); 1.1069 + 1.1070 + if (initializeColorBuffer) { 1.1071 + 1.1072 + if (drawBuffersIsEnabled) { 1.1073 + 1.1074 + GLenum drawBuffersCommand[WebGLContext::sMaxColorAttachments] = { LOCAL_GL_NONE }; 1.1075 + 1.1076 + for(int32_t i = 0; i < mGLMaxDrawBuffers; i++) { 1.1077 + GLint temp; 1.1078 + gl->fGetIntegerv(LOCAL_GL_DRAW_BUFFER0 + i, &temp); 1.1079 + currentDrawBuffers[i] = temp; 1.1080 + 1.1081 + if (colorAttachmentsMask[i]) { 1.1082 + drawBuffersCommand[i] = LOCAL_GL_COLOR_ATTACHMENT0 + i; 1.1083 + } 1.1084 + } 1.1085 + 1.1086 + gl->fDrawBuffers(mGLMaxDrawBuffers, drawBuffersCommand); 1.1087 + } 1.1088 + 1.1089 + gl->fColorMask(1, 1, 1, 1); 1.1090 + gl->fClearColor(0.0f, 0.0f, 0.0f, 0.0f); 1.1091 + } 1.1092 + 1.1093 + if (initializeDepthBuffer) { 1.1094 + gl->fDepthMask(1); 1.1095 + gl->fClearDepth(1.0f); 1.1096 + } 1.1097 + 1.1098 + if (initializeStencilBuffer) { 1.1099 + // "The clear operation always uses the front stencil write mask 1.1100 + // when clearing the stencil buffer." 1.1101 + gl->fStencilMaskSeparate(LOCAL_GL_FRONT, 0xffffffff); 1.1102 + gl->fStencilMaskSeparate(LOCAL_GL_BACK, 0xffffffff); 1.1103 + gl->fClearStencil(0); 1.1104 + } 1.1105 + 1.1106 + if (mRasterizerDiscardEnabled) { 1.1107 + gl->fDisable(LOCAL_GL_RASTERIZER_DISCARD); 1.1108 + } 1.1109 + 1.1110 + // Do the clear! 1.1111 + gl->fClear(mask); 1.1112 + 1.1113 + // And reset! 1.1114 + if (mScissorTestEnabled) 1.1115 + gl->fEnable(LOCAL_GL_SCISSOR_TEST); 1.1116 + 1.1117 + if (mRasterizerDiscardEnabled) { 1.1118 + gl->fEnable(LOCAL_GL_RASTERIZER_DISCARD); 1.1119 + } 1.1120 + 1.1121 + // Restore GL state after clearing. 1.1122 + if (initializeColorBuffer) { 1.1123 + if (drawBuffersIsEnabled) { 1.1124 + gl->fDrawBuffers(mGLMaxDrawBuffers, currentDrawBuffers); 1.1125 + } 1.1126 + 1.1127 + gl->fColorMask(mColorWriteMask[0], 1.1128 + mColorWriteMask[1], 1.1129 + mColorWriteMask[2], 1.1130 + mColorWriteMask[3]); 1.1131 + gl->fClearColor(mColorClearValue[0], 1.1132 + mColorClearValue[1], 1.1133 + mColorClearValue[2], 1.1134 + mColorClearValue[3]); 1.1135 + } 1.1136 + 1.1137 + if (initializeDepthBuffer) { 1.1138 + gl->fDepthMask(mDepthWriteMask); 1.1139 + gl->fClearDepth(mDepthClearValue); 1.1140 + } 1.1141 + 1.1142 + if (initializeStencilBuffer) { 1.1143 + gl->fStencilMaskSeparate(LOCAL_GL_FRONT, mStencilWriteMaskFront); 1.1144 + gl->fStencilMaskSeparate(LOCAL_GL_BACK, mStencilWriteMaskBack); 1.1145 + gl->fClearStencil(mStencilClearValue); 1.1146 + } 1.1147 +} 1.1148 + 1.1149 +// For an overview of how WebGL compositing works, see: 1.1150 +// https://wiki.mozilla.org/Platform/GFX/WebGL/Compositing 1.1151 +bool 1.1152 +WebGLContext::PresentScreenBuffer() 1.1153 +{ 1.1154 + if (IsContextLost()) { 1.1155 + return false; 1.1156 + } 1.1157 + 1.1158 + if (!mShouldPresent) { 1.1159 + return false; 1.1160 + } 1.1161 + 1.1162 + gl->MakeCurrent(); 1.1163 + MOZ_ASSERT(!mBackbufferNeedsClear); 1.1164 + if (!gl->PublishFrame()) { 1.1165 + this->ForceLoseContext(); 1.1166 + return false; 1.1167 + } 1.1168 + 1.1169 + if (!mOptions.preserveDrawingBuffer) { 1.1170 + mBackbufferNeedsClear = true; 1.1171 + } 1.1172 + 1.1173 + mShouldPresent = false; 1.1174 + 1.1175 + return true; 1.1176 +} 1.1177 + 1.1178 +void 1.1179 +WebGLContext::DummyFramebufferOperation(const char *info) 1.1180 +{ 1.1181 + GLenum status = CheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER); 1.1182 + if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) 1.1183 + ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info); 1.1184 +} 1.1185 + 1.1186 +// We use this timer for many things. Here are the things that it is activated for: 1.1187 +// 1) If a script is using the MOZ_WEBGL_lose_context extension. 1.1188 +// 2) If we are using EGL and _NOT ANGLE_, we query periodically to see if the 1.1189 +// CONTEXT_LOST_WEBGL error has been triggered. 1.1190 +// 3) If we are using ANGLE, or anything that supports ARB_robustness, query the 1.1191 +// GPU periodically to see if the reset status bit has been set. 1.1192 +// In all of these situations, we use this timer to send the script context lost 1.1193 +// and restored events asynchronously. For example, if it triggers a context loss, 1.1194 +// the webglcontextlost event will be sent to it the next time the robustness timer 1.1195 +// fires. 1.1196 +// Note that this timer mechanism is not used unless one of these 3 criteria 1.1197 +// are met. 1.1198 +// At a bare minimum, from context lost to context restores, it would take 3 1.1199 +// full timer iterations: detection, webglcontextlost, webglcontextrestored. 1.1200 +void 1.1201 +WebGLContext::RobustnessTimerCallback(nsITimer* timer) 1.1202 +{ 1.1203 + TerminateContextLossTimer(); 1.1204 + 1.1205 + if (!mCanvasElement) { 1.1206 + // the canvas is gone. That happens when the page was closed before we got 1.1207 + // this timer event. In this case, there's nothing to do here, just don't crash. 1.1208 + return; 1.1209 + } 1.1210 + 1.1211 + // If the context has been lost and we're waiting for it to be restored, do 1.1212 + // that now. 1.1213 + if (mContextStatus == ContextLostAwaitingEvent) { 1.1214 + bool defaultAction; 1.1215 + nsContentUtils::DispatchTrustedEvent(mCanvasElement->OwnerDoc(), 1.1216 + static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement), 1.1217 + NS_LITERAL_STRING("webglcontextlost"), 1.1218 + true, 1.1219 + true, 1.1220 + &defaultAction); 1.1221 + 1.1222 + // If the script didn't handle the event, we don't allow restores. 1.1223 + if (defaultAction) 1.1224 + mAllowRestore = false; 1.1225 + 1.1226 + // If the script handled the event and we are allowing restores, then 1.1227 + // mark it to be restored. Otherwise, leave it as context lost 1.1228 + // (unusable). 1.1229 + if (!defaultAction && mAllowRestore) { 1.1230 + ForceRestoreContext(); 1.1231 + // Restart the timer so that it will be restored on the next 1.1232 + // callback. 1.1233 + SetupContextLossTimer(); 1.1234 + } else { 1.1235 + mContextStatus = ContextLost; 1.1236 + } 1.1237 + } else if (mContextStatus == ContextLostAwaitingRestore) { 1.1238 + // Try to restore the context. If it fails, try again later. 1.1239 + if (NS_FAILED(SetDimensions(mWidth, mHeight))) { 1.1240 + SetupContextLossTimer(); 1.1241 + return; 1.1242 + } 1.1243 + mContextStatus = ContextNotLost; 1.1244 + nsContentUtils::DispatchTrustedEvent(mCanvasElement->OwnerDoc(), 1.1245 + static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement), 1.1246 + NS_LITERAL_STRING("webglcontextrestored"), 1.1247 + true, 1.1248 + true); 1.1249 + // Set all flags back to the state they were in before the context was 1.1250 + // lost. 1.1251 + mEmitContextLostErrorOnce = true; 1.1252 + mAllowRestore = true; 1.1253 + } 1.1254 + 1.1255 + MaybeRestoreContext(); 1.1256 + return; 1.1257 +} 1.1258 + 1.1259 +void 1.1260 +WebGLContext::MaybeRestoreContext() 1.1261 +{ 1.1262 + // Don't try to handle it if we already know it's busted. 1.1263 + if (mContextStatus != ContextNotLost || gl == nullptr) 1.1264 + return; 1.1265 + 1.1266 + bool isEGL = gl->GetContextType() == gl::GLContextType::EGL, 1.1267 + isANGLE = gl->IsANGLE(); 1.1268 + 1.1269 + GLContext::ContextResetARB resetStatus = GLContext::CONTEXT_NO_ERROR; 1.1270 + if (mHasRobustness) { 1.1271 + gl->MakeCurrent(); 1.1272 + resetStatus = (GLContext::ContextResetARB) gl->fGetGraphicsResetStatus(); 1.1273 + } else if (isEGL) { 1.1274 + // Simulate a ARB_robustness guilty context loss for when we 1.1275 + // get an EGL_CONTEXT_LOST error. It may not actually be guilty, 1.1276 + // but we can't make any distinction, so we must assume the worst 1.1277 + // case. 1.1278 + if (!gl->MakeCurrent(true) && gl->IsContextLost()) { 1.1279 + resetStatus = GLContext::CONTEXT_GUILTY_CONTEXT_RESET_ARB; 1.1280 + } 1.1281 + } 1.1282 + 1.1283 + if (resetStatus != GLContext::CONTEXT_NO_ERROR) { 1.1284 + // It's already lost, but clean up after it and signal to JS that it is 1.1285 + // lost. 1.1286 + ForceLoseContext(); 1.1287 + } 1.1288 + 1.1289 + switch (resetStatus) { 1.1290 + case GLContext::CONTEXT_NO_ERROR: 1.1291 + // If there has been activity since the timer was set, it's possible 1.1292 + // that we did or are going to miss something, so clear this flag and 1.1293 + // run it again some time later. 1.1294 + if (mDrawSinceContextLossTimerSet) 1.1295 + SetupContextLossTimer(); 1.1296 + break; 1.1297 + case GLContext::CONTEXT_GUILTY_CONTEXT_RESET_ARB: 1.1298 + NS_WARNING("WebGL content on the page caused the graphics card to reset; not restoring the context"); 1.1299 + mAllowRestore = false; 1.1300 + break; 1.1301 + case GLContext::CONTEXT_INNOCENT_CONTEXT_RESET_ARB: 1.1302 + break; 1.1303 + case GLContext::CONTEXT_UNKNOWN_CONTEXT_RESET_ARB: 1.1304 + NS_WARNING("WebGL content on the page might have caused the graphics card to reset"); 1.1305 + if (isEGL && isANGLE) { 1.1306 + // If we're using ANGLE, we ONLY get back UNKNOWN context resets, including for guilty contexts. 1.1307 + // This means that we can't restore it or risk restoring a guilty context. Should this ever change, 1.1308 + // we can get rid of the whole IsANGLE() junk from GLContext.h since, as of writing, this is the 1.1309 + // only use for it. See ANGLE issue 261. 1.1310 + mAllowRestore = false; 1.1311 + } 1.1312 + break; 1.1313 + } 1.1314 +} 1.1315 + 1.1316 +void 1.1317 +WebGLContext::ForceLoseContext() 1.1318 +{ 1.1319 + if (mContextStatus == ContextLostAwaitingEvent) 1.1320 + return; 1.1321 + 1.1322 + mContextStatus = ContextLostAwaitingEvent; 1.1323 + // Queue up a task to restore the event. 1.1324 + SetupContextLossTimer(); 1.1325 + DestroyResourcesAndContext(); 1.1326 +} 1.1327 + 1.1328 +void 1.1329 +WebGLContext::ForceRestoreContext() 1.1330 +{ 1.1331 + mContextStatus = ContextLostAwaitingRestore; 1.1332 +} 1.1333 + 1.1334 +void 1.1335 +WebGLContext::MakeContextCurrent() const { gl->MakeCurrent(); } 1.1336 + 1.1337 +mozilla::TemporaryRef<mozilla::gfx::SourceSurface> 1.1338 +WebGLContext::GetSurfaceSnapshot(bool* aPremultAlpha) 1.1339 +{ 1.1340 + if (!gl) 1.1341 + return nullptr; 1.1342 + 1.1343 + nsRefPtr<gfxImageSurface> surf = new gfxImageSurface(gfxIntSize(mWidth, mHeight), 1.1344 + gfxImageFormat::ARGB32, 1.1345 + mWidth * 4, 0, false); 1.1346 + if (surf->CairoStatus() != 0) { 1.1347 + return nullptr; 1.1348 + } 1.1349 + 1.1350 + gl->MakeCurrent(); 1.1351 + { 1.1352 + ScopedBindFramebuffer autoFB(gl, 0); 1.1353 + ClearBackbufferIfNeeded(); 1.1354 + ReadPixelsIntoImageSurface(gl, surf); 1.1355 + } 1.1356 + 1.1357 + if (aPremultAlpha) { 1.1358 + *aPremultAlpha = true; 1.1359 + } 1.1360 + bool srcPremultAlpha = mOptions.premultipliedAlpha; 1.1361 + if (!srcPremultAlpha) { 1.1362 + if (aPremultAlpha) { 1.1363 + *aPremultAlpha = false; 1.1364 + } else { 1.1365 + gfxUtils::PremultiplyImageSurface(surf); 1.1366 + surf->MarkDirty(); 1.1367 + } 1.1368 + } 1.1369 + 1.1370 + RefPtr<DrawTarget> dt = 1.1371 + Factory::CreateDrawTarget(BackendType::CAIRO, 1.1372 + IntSize(mWidth, mHeight), 1.1373 + SurfaceFormat::B8G8R8A8); 1.1374 + 1.1375 + if (!dt) { 1.1376 + return nullptr; 1.1377 + } 1.1378 + 1.1379 + RefPtr<SourceSurface> source = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, surf); 1.1380 + 1.1381 + Matrix m; 1.1382 + m.Translate(0.0, mHeight); 1.1383 + m.Scale(1.0, -1.0); 1.1384 + dt->SetTransform(m); 1.1385 + 1.1386 + dt->DrawSurface(source, 1.1387 + Rect(0, 0, mWidth, mHeight), 1.1388 + Rect(0, 0, mWidth, mHeight), 1.1389 + DrawSurfaceOptions(), 1.1390 + DrawOptions(1.0f, CompositionOp::OP_SOURCE)); 1.1391 + 1.1392 + return dt->Snapshot(); 1.1393 +} 1.1394 + 1.1395 +// 1.1396 +// XPCOM goop 1.1397 +// 1.1398 + 1.1399 +NS_IMPL_CYCLE_COLLECTING_ADDREF(WebGLContext) 1.1400 +NS_IMPL_CYCLE_COLLECTING_RELEASE(WebGLContext) 1.1401 + 1.1402 +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_13(WebGLContext, 1.1403 + mCanvasElement, 1.1404 + mExtensions, 1.1405 + mBound2DTextures, 1.1406 + mBoundCubeMapTextures, 1.1407 + mBoundArrayBuffer, 1.1408 + mBoundTransformFeedbackBuffer, 1.1409 + mCurrentProgram, 1.1410 + mBoundFramebuffer, 1.1411 + mBoundRenderbuffer, 1.1412 + mBoundVertexArray, 1.1413 + mDefaultVertexArray, 1.1414 + mActiveOcclusionQuery, 1.1415 + mActiveTransformFeedbackQuery) 1.1416 + 1.1417 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebGLContext) 1.1418 + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 1.1419 + NS_INTERFACE_MAP_ENTRY(nsIDOMWebGLRenderingContext) 1.1420 + NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal) 1.1421 + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) 1.1422 + // If the exact way we cast to nsISupports here ever changes, fix our 1.1423 + // ToSupports() method. 1.1424 + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMWebGLRenderingContext) 1.1425 +NS_INTERFACE_MAP_END