content/canvas/src/WebGLContext.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

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.)

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

mercurial