Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/canvas/WebGLContext.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this
4
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
#include "WebGLContext.h"
7
8
#include <algorithm>
9
#include <queue>
10
11
#include "AccessCheck.h"
12
#include "gfxContext.h"
13
#include "gfxCrashReporterUtils.h"
14
#include "gfxPattern.h"
15
#include "gfxPrefs.h"
16
#include "gfxUtils.h"
17
#include "MozFramebuffer.h"
18
#include "GLBlitHelper.h"
19
#include "GLContext.h"
20
#include "GLContextProvider.h"
21
#include "GLReadTexImageHelper.h"
22
#include "GLScreenBuffer.h"
23
#include "ImageContainer.h"
24
#include "ImageEncoder.h"
25
#include "Layers.h"
26
#include "LayerUserData.h"
27
#include "mozilla/dom/BindingUtils.h"
28
#include "mozilla/dom/Event.h"
29
#include "mozilla/dom/HTMLVideoElement.h"
30
#include "mozilla/dom/ImageData.h"
31
#include "mozilla/dom/WebGLContextEvent.h"
32
#include "mozilla/EnumeratedArrayCycleCollection.h"
33
#include "mozilla/Preferences.h"
34
#include "mozilla/ProcessPriorityManager.h"
35
#include "mozilla/ScopeExit.h"
36
#include "mozilla/Services.h"
37
#include "mozilla/Telemetry.h"
38
#include "nsContentUtils.h"
39
#include "nsDisplayList.h"
40
#include "nsError.h"
41
#include "nsIClassInfoImpl.h"
42
#include "nsIConsoleService.h"
43
#include "nsIGfxInfo.h"
44
#include "nsIObserverService.h"
45
#include "nsIVariant.h"
46
#include "nsIWidget.h"
47
#include "nsIXPConnect.h"
48
#include "nsServiceManagerUtils.h"
49
#include "SVGObserverUtils.h"
50
#include "prenv.h"
51
#include "ScopedGLHelpers.h"
52
#include "VRManagerChild.h"
53
#include "mozilla/layers/TextureClientSharedSurface.h"
54
#include "mozilla/layers/WebRenderUserData.h"
55
#include "mozilla/layers/WebRenderCanvasRenderer.h"
56
57
// Local
58
#include "CanvasUtils.h"
59
#include "WebGL1Context.h"
60
#include "WebGLActiveInfo.h"
61
#include "WebGLBuffer.h"
62
#include "WebGLContextLossHandler.h"
63
#include "WebGLContextUtils.h"
64
#include "WebGLExtensions.h"
65
#include "WebGLFramebuffer.h"
66
#include "WebGLMemoryTracker.h"
67
#include "WebGLObjectModel.h"
68
#include "WebGLProgram.h"
69
#include "WebGLQuery.h"
70
#include "WebGLSampler.h"
71
#include "WebGLShader.h"
72
#include "WebGLSync.h"
73
#include "WebGLTransformFeedback.h"
74
#include "WebGLVertexArray.h"
75
#include "WebGLVertexAttribData.h"
76
77
#ifdef MOZ_WIDGET_COCOA
78
#include "nsCocoaFeatures.h"
79
#endif
80
81
#ifdef XP_WIN
82
#include "WGLLibrary.h"
83
#endif
84
85
#if defined(MOZ_WIDGET_ANDROID)
86
#include "mozilla/layers/ImageBridgeChild.h"
87
#endif
88
89
// Generated
90
#include "mozilla/dom/WebGLRenderingContextBinding.h"
91
92
93
namespace mozilla {
94
95
using namespace mozilla::dom;
96
using namespace mozilla::gfx;
97
using namespace mozilla::gl;
98
using namespace mozilla::layers;
99
100
WebGLContextOptions::WebGLContextOptions()
101
0
{
102
0
    // Set default alpha state based on preference.
103
0
    if (gfxPrefs::WebGLDefaultNoAlpha())
104
0
        alpha = false;
105
0
}
106
107
bool
108
WebGLContextOptions::operator==(const WebGLContextOptions& r) const
109
0
{
110
0
    bool eq = true;
111
0
    eq &= (alpha == r.alpha);
112
0
    eq &= (depth == r.depth);
113
0
    eq &= (stencil == r.stencil);
114
0
    eq &= (premultipliedAlpha == r.premultipliedAlpha);
115
0
    eq &= (antialias == r.antialias);
116
0
    eq &= (preserveDrawingBuffer == r.preserveDrawingBuffer);
117
0
    eq &= (failIfMajorPerformanceCaveat == r.failIfMajorPerformanceCaveat);
118
0
    eq &= (powerPreference == r.powerPreference);
119
0
    return eq;
120
0
}
121
122
WebGLContext::WebGLContext()
123
    : WebGLContextUnchecked(nullptr)
124
    , mMaxPerfWarnings(gfxPrefs::WebGLMaxPerfWarnings())
125
    , mNumPerfWarnings(0)
126
    , mMaxAcceptableFBStatusInvals(gfxPrefs::WebGLMaxAcceptableFBStatusInvals())
127
    , mDataAllocGLCallCount(0)
128
    , mBypassShaderValidation(false)
129
    , mEmptyTFO(0)
130
    , mContextLossHandler(this)
131
    , mNeedsFakeNoAlpha(false)
132
    , mNeedsFakeNoDepth(false)
133
    , mNeedsFakeNoStencil(false)
134
    , mAllowFBInvalidation(gfxPrefs::WebGLFBInvalidation())
135
    , mMsaaSamples(gfxPrefs::WebGLMsaaSamples())
136
0
{
137
0
    mGeneration = 0;
138
0
    mInvalidated = false;
139
0
    mCapturedFrameInvalidated = false;
140
0
    mShouldPresent = true;
141
0
    mResetLayer = true;
142
0
    mOptionsFrozen = false;
143
0
    mDisableExtensions = false;
144
0
    mIsMesa = false;
145
0
    mEmitContextLostErrorOnce = false;
146
0
    mWebGLError = 0;
147
0
    mUnderlyingGLError = 0;
148
0
149
0
    mContextLostErrorSet = false;
150
0
151
0
    mViewportX = 0;
152
0
    mViewportY = 0;
153
0
    mViewportWidth = 0;
154
0
    mViewportHeight = 0;
155
0
156
0
    mDitherEnabled = 1;
157
0
    mRasterizerDiscardEnabled = 0; // OpenGL ES 3.0 spec p244
158
0
    mScissorTestEnabled = 0;
159
0
    mStencilTestEnabled = 0;
160
0
161
0
    if (NS_IsMainThread()) {
162
0
        // XXX mtseng: bug 709490, not thread safe
163
0
        WebGLMemoryTracker::AddWebGLContext(this);
164
0
    }
165
0
166
0
    mAllowContextRestore = true;
167
0
    mLastLossWasSimulated = false;
168
0
    mLoseContextOnMemoryPressure = false;
169
0
    mCanLoseContextInForeground = true;
170
0
    mRestoreWhenVisible = false;
171
0
172
0
    mAlreadyGeneratedWarnings = 0;
173
0
    mAlreadyWarnedAboutFakeVertexAttrib0 = false;
174
0
    mAlreadyWarnedAboutViewportLargerThanDest = false;
175
0
176
0
    mMaxWarnings = gfxPrefs::WebGLMaxWarningsPerContext();
177
0
    if (mMaxWarnings < -1) {
178
0
        GenerateWarning("webgl.max-warnings-per-context size is too large (seems like a negative value wrapped)");
179
0
        mMaxWarnings = 0;
180
0
    }
181
0
182
0
    mLastUseIndex = 0;
183
0
184
0
    mDisableFragHighP = false;
185
0
186
0
    mDrawCallsSinceLastFlush = 0;
187
0
}
188
189
WebGLContext::~WebGLContext()
190
0
{
191
0
    RemovePostRefreshObserver();
192
0
193
0
    DestroyResourcesAndContext();
194
0
    if (NS_IsMainThread()) {
195
0
        // XXX mtseng: bug 709490, not thread safe
196
0
        WebGLMemoryTracker::RemoveWebGLContext(this);
197
0
    }
198
0
}
199
200
template<typename T>
201
void
202
ClearLinkedList(LinkedList<T>& list)
203
0
{
204
0
    while (!list.isEmpty()) {
205
0
        list.getLast()->DeleteOnce();
206
0
    }
207
0
}
Unexecuted instantiation: void mozilla::ClearLinkedList<mozilla::WebGLBuffer>(mozilla::LinkedList<mozilla::WebGLBuffer>&)
Unexecuted instantiation: void mozilla::ClearLinkedList<mozilla::WebGLFramebuffer>(mozilla::LinkedList<mozilla::WebGLFramebuffer>&)
Unexecuted instantiation: void mozilla::ClearLinkedList<mozilla::WebGLProgram>(mozilla::LinkedList<mozilla::WebGLProgram>&)
Unexecuted instantiation: void mozilla::ClearLinkedList<mozilla::WebGLQuery>(mozilla::LinkedList<mozilla::WebGLQuery>&)
Unexecuted instantiation: void mozilla::ClearLinkedList<mozilla::WebGLRenderbuffer>(mozilla::LinkedList<mozilla::WebGLRenderbuffer>&)
Unexecuted instantiation: void mozilla::ClearLinkedList<mozilla::WebGLSampler>(mozilla::LinkedList<mozilla::WebGLSampler>&)
Unexecuted instantiation: void mozilla::ClearLinkedList<mozilla::WebGLShader>(mozilla::LinkedList<mozilla::WebGLShader>&)
Unexecuted instantiation: void mozilla::ClearLinkedList<mozilla::WebGLSync>(mozilla::LinkedList<mozilla::WebGLSync>&)
Unexecuted instantiation: void mozilla::ClearLinkedList<mozilla::WebGLTexture>(mozilla::LinkedList<mozilla::WebGLTexture>&)
Unexecuted instantiation: void mozilla::ClearLinkedList<mozilla::WebGLTransformFeedback>(mozilla::LinkedList<mozilla::WebGLTransformFeedback>&)
Unexecuted instantiation: void mozilla::ClearLinkedList<mozilla::WebGLVertexArray>(mozilla::LinkedList<mozilla::WebGLVertexArray>&)
208
209
void
210
WebGLContext::DestroyResourcesAndContext()
211
0
{
212
0
    if (!gl)
213
0
        return;
214
0
215
0
    mDefaultFB = nullptr;
216
0
    mResolvedDefaultFB = nullptr;
217
0
218
0
    mBound2DTextures.Clear();
219
0
    mBoundCubeMapTextures.Clear();
220
0
    mBound3DTextures.Clear();
221
0
    mBound2DArrayTextures.Clear();
222
0
    mBoundSamplers.Clear();
223
0
    mBoundArrayBuffer = nullptr;
224
0
    mBoundCopyReadBuffer = nullptr;
225
0
    mBoundCopyWriteBuffer = nullptr;
226
0
    mBoundPixelPackBuffer = nullptr;
227
0
    mBoundPixelUnpackBuffer = nullptr;
228
0
    mBoundTransformFeedbackBuffer = nullptr;
229
0
    mBoundUniformBuffer = nullptr;
230
0
    mCurrentProgram = nullptr;
231
0
    mActiveProgramLinkInfo = nullptr;
232
0
    mBoundDrawFramebuffer = nullptr;
233
0
    mBoundReadFramebuffer = nullptr;
234
0
    mBoundRenderbuffer = nullptr;
235
0
    mBoundVertexArray = nullptr;
236
0
    mDefaultVertexArray = nullptr;
237
0
    mBoundTransformFeedback = nullptr;
238
0
    mDefaultTransformFeedback = nullptr;
239
#if defined(MOZ_WIDGET_ANDROID)
240
    mVRScreen = nullptr;
241
#endif
242
243
0
    mQuerySlot_SamplesPassed = nullptr;
244
0
    mQuerySlot_TFPrimsWritten = nullptr;
245
0
    mQuerySlot_TimeElapsed = nullptr;
246
0
247
0
    mIndexedUniformBufferBindings.clear();
248
0
249
0
    if (mAvailabilityRunnable) {
250
0
        mAvailabilityRunnable->Run();
251
0
    }
252
0
253
0
    //////
254
0
255
0
    ClearLinkedList(mBuffers);
256
0
    ClearLinkedList(mFramebuffers);
257
0
    ClearLinkedList(mPrograms);
258
0
    ClearLinkedList(mQueries);
259
0
    ClearLinkedList(mRenderbuffers);
260
0
    ClearLinkedList(mSamplers);
261
0
    ClearLinkedList(mShaders);
262
0
    ClearLinkedList(mSyncs);
263
0
    ClearLinkedList(mTextures);
264
0
    ClearLinkedList(mTransformFeedbacks);
265
0
    ClearLinkedList(mVertexArrays);
266
0
267
0
    //////
268
0
269
0
    if (mEmptyTFO) {
270
0
        gl->fDeleteTransformFeedbacks(1, &mEmptyTFO);
271
0
        mEmptyTFO = 0;
272
0
    }
273
0
274
0
    //////
275
0
276
0
    mFakeBlack_2D_0000       = nullptr;
277
0
    mFakeBlack_2D_0001       = nullptr;
278
0
    mFakeBlack_CubeMap_0000  = nullptr;
279
0
    mFakeBlack_CubeMap_0001  = nullptr;
280
0
    mFakeBlack_3D_0000       = nullptr;
281
0
    mFakeBlack_3D_0001       = nullptr;
282
0
    mFakeBlack_2D_Array_0000 = nullptr;
283
0
    mFakeBlack_2D_Array_0001 = nullptr;
284
0
285
0
    if (mFakeVertexAttrib0BufferObject) {
286
0
        gl->fDeleteBuffers(1, &mFakeVertexAttrib0BufferObject);
287
0
        mFakeVertexAttrib0BufferObject = 0;
288
0
    }
289
0
290
0
    // disable all extensions except "WEBGL_lose_context". see bug #927969
291
0
    // spec: http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
292
0
    for (size_t i = 0; i < size_t(WebGLExtensionID::Max); ++i) {
293
0
        WebGLExtensionID extension = WebGLExtensionID(i);
294
0
295
0
        if (!IsExtensionEnabled(extension) || (extension == WebGLExtensionID::WEBGL_lose_context))
296
0
            continue;
297
0
298
0
        mExtensions[extension]->MarkLost();
299
0
        mExtensions[extension] = nullptr;
300
0
    }
301
0
302
0
    // We just got rid of everything, so the context had better
303
0
    // have been going away.
304
0
    if (GLContext::ShouldSpew()) {
305
0
        printf_stderr("--- WebGL context destroyed: %p\n", gl.get());
306
0
    }
307
0
308
0
    MOZ_ASSERT(gl);
309
0
    gl->MarkDestroyed();
310
0
    mGL_OnlyClearInDestroyResourcesAndContext = nullptr;
311
0
    MOZ_ASSERT(!gl);
312
0
}
313
314
void
315
WebGLContext::Invalidate()
316
0
{
317
0
    if (!mCanvasElement)
318
0
        return;
319
0
320
0
    mCapturedFrameInvalidated = true;
321
0
322
0
    if (mInvalidated)
323
0
        return;
324
0
325
0
    SVGObserverUtils::InvalidateDirectRenderingObservers(mCanvasElement);
326
0
327
0
    mInvalidated = true;
328
0
    mCanvasElement->InvalidateCanvasContent(nullptr);
329
0
}
330
331
void
332
WebGLContext::OnVisibilityChange()
333
0
{
334
0
    if (gl) // Context not lost.
335
0
        return;
336
0
337
0
    if (!mRestoreWhenVisible || mLastLossWasSimulated) {
338
0
        return;
339
0
    }
340
0
341
0
    ForceRestoreContext();
342
0
}
343
344
void
345
WebGLContext::OnMemoryPressure()
346
0
{
347
0
    bool shouldLoseContext = mLoseContextOnMemoryPressure;
348
0
349
0
    if (!mCanLoseContextInForeground &&
350
0
        ProcessPriorityManager::CurrentProcessIsForeground())
351
0
    {
352
0
        shouldLoseContext = false;
353
0
    }
354
0
355
0
    if (shouldLoseContext)
356
0
        ForceLoseContext();
357
0
}
358
359
//
360
// nsICanvasRenderingContextInternal
361
//
362
363
static bool
364
IsFeatureInBlacklist(const nsCOMPtr<nsIGfxInfo>& gfxInfo, int32_t feature,
365
                     nsCString* const out_blacklistId)
366
0
{
367
0
    int32_t status;
368
0
    if (!NS_SUCCEEDED(gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo, feature,
369
0
                                                           *out_blacklistId, &status)))
370
0
    {
371
0
        return false;
372
0
    }
373
0
374
0
    return status != nsIGfxInfo::FEATURE_STATUS_OK;
375
0
}
376
377
NS_IMETHODIMP
378
WebGLContext::SetContextOptions(JSContext* cx, JS::Handle<JS::Value> options,
379
                                ErrorResult& aRvForDictionaryInit)
380
0
{
381
0
    const FuncScope funcScope(*this, "getContext");
382
0
    (void)IsContextLost(); // Ignore this.
383
0
384
0
    if (options.isNullOrUndefined() && mOptionsFrozen)
385
0
        return NS_OK;
386
0
387
0
    WebGLContextAttributes attributes;
388
0
    if (!attributes.Init(cx, options)) {
389
0
      aRvForDictionaryInit.Throw(NS_ERROR_UNEXPECTED);
390
0
      return NS_ERROR_UNEXPECTED;
391
0
    }
392
0
393
0
    WebGLContextOptions newOpts;
394
0
395
0
    newOpts.stencil = attributes.mStencil;
396
0
    newOpts.depth = attributes.mDepth;
397
0
    newOpts.premultipliedAlpha = attributes.mPremultipliedAlpha;
398
0
    newOpts.antialias = attributes.mAntialias;
399
0
    newOpts.preserveDrawingBuffer = attributes.mPreserveDrawingBuffer;
400
0
    newOpts.failIfMajorPerformanceCaveat = attributes.mFailIfMajorPerformanceCaveat;
401
0
    newOpts.powerPreference = attributes.mPowerPreference;
402
0
403
0
    if (attributes.mAlpha.WasPassed()) {
404
0
        newOpts.alpha = attributes.mAlpha.Value();
405
0
    }
406
0
407
0
    // Don't do antialiasing if we've disabled MSAA.
408
0
    if (!gfxPrefs::MSAALevel()) {
409
0
        newOpts.antialias = false;
410
0
    }
411
0
412
0
    if (!gfxPrefs::WebGLForceMSAA()) {
413
0
        const nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
414
0
415
0
        nsCString blocklistId;
416
0
        if (IsFeatureInBlacklist(gfxInfo, nsIGfxInfo::FEATURE_WEBGL_MSAA, &blocklistId)) {
417
0
            GenerateWarning("Disallowing antialiased backbuffers due to blacklisting.");
418
0
            newOpts.antialias = false;
419
0
        }
420
0
    }
421
0
422
#if 0
423
    GenerateWarning("aaHint: %d stencil: %d depth: %d alpha: %d premult: %d preserve: %d\n",
424
               newOpts.antialias ? 1 : 0,
425
               newOpts.stencil ? 1 : 0,
426
               newOpts.depth ? 1 : 0,
427
               newOpts.alpha ? 1 : 0,
428
               newOpts.premultipliedAlpha ? 1 : 0,
429
               newOpts.preserveDrawingBuffer ? 1 : 0);
430
#endif
431
432
0
    if (mOptionsFrozen && !(newOpts == mOptions)) {
433
0
        // Error if the options are already frozen, and the ones that were asked for
434
0
        // aren't the same as what they were originally.
435
0
        return NS_ERROR_FAILURE;
436
0
    }
437
0
438
0
    mOptions = newOpts;
439
0
    return NS_OK;
440
0
}
441
442
static bool
443
HasAcceleratedLayers(const nsCOMPtr<nsIGfxInfo>& gfxInfo)
444
0
{
445
0
    int32_t status;
446
0
447
0
    nsCString discardFailureId;
448
0
    gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
449
0
                                         nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS,
450
0
                                         discardFailureId,
451
0
                                         &status);
452
0
    if (status)
453
0
        return true;
454
0
    gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
455
0
                                         nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS,
456
0
                                         discardFailureId,
457
0
                                         &status);
458
0
    if (status)
459
0
        return true;
460
0
    gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
461
0
                                         nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS,
462
0
                                         discardFailureId,
463
0
                                         &status);
464
0
    if (status)
465
0
        return true;
466
0
    gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
467
0
                                         nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS,
468
0
                                         discardFailureId,
469
0
                                         &status);
470
0
    if (status)
471
0
        return true;
472
0
    gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
473
0
                                         nsIGfxInfo::FEATURE_OPENGL_LAYERS,
474
0
                                         discardFailureId,
475
0
                                         &status);
476
0
    if (status)
477
0
        return true;
478
0
479
0
    return false;
480
0
}
481
482
// --
483
484
bool
485
WebGLContext::CreateAndInitGL(bool forceEnabled,
486
                              std::vector<FailureReason>* const out_failReasons)
487
0
{
488
0
    // Can't use WebGL in headless mode.
489
0
    if (gfxPlatform::IsHeadless()) {
490
0
        FailureReason reason;
491
0
        reason.info = "Can't use WebGL in headless mode (https://bugzil.la/1375585).";
492
0
        out_failReasons->push_back(reason);
493
0
        GenerateWarning("%s", reason.info.BeginReading());
494
0
        return false;
495
0
    }
496
0
497
0
    // WebGL2 is separately blocked:
498
0
    if (IsWebGL2()) {
499
0
        const nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
500
0
        const auto feature = nsIGfxInfo::FEATURE_WEBGL2;
501
0
502
0
        FailureReason reason;
503
0
        if (IsFeatureInBlacklist(gfxInfo, feature, &reason.key)) {
504
0
            reason.info = "Refused to create WebGL2 context because of blacklist"
505
0
                          " entry: ";
506
0
            reason.info.Append(reason.key);
507
0
            out_failReasons->push_back(reason);
508
0
            GenerateWarning("%s", reason.info.BeginReading());
509
0
            return false;
510
0
        }
511
0
    }
512
0
513
0
    gl::CreateContextFlags flags = (gl::CreateContextFlags::NO_VALIDATION |
514
0
                                    gl::CreateContextFlags::PREFER_ROBUSTNESS);
515
0
    bool tryNativeGL = true;
516
0
    bool tryANGLE = false;
517
0
518
0
    if (forceEnabled) {
519
0
        flags |= gl::CreateContextFlags::FORCE_ENABLE_HARDWARE;
520
0
    }
521
0
522
0
    if (IsWebGL2()) {
523
0
        flags |= gl::CreateContextFlags::PREFER_ES3;
524
0
    } else if (!gfxPrefs::WebGL1AllowCoreProfile()) {
525
0
        flags |= gl::CreateContextFlags::REQUIRE_COMPAT_PROFILE;
526
0
    }
527
0
528
0
    switch (mOptions.powerPreference) {
529
0
    case dom::WebGLPowerPreference::Low_power:
530
0
        break;
531
0
532
0
        // Eventually add a heuristic, but for now default to high-performance.
533
0
        // We can even make it dynamic by holding on to a ForceDiscreteGPUHelperCGL iff
534
0
        // we decide it's a high-performance application:
535
0
        // - Non-trivial canvas size
536
0
        // - Many draw calls
537
0
        // - Same origin with root page (try to stem bleeding from WebGL ads/trackers)
538
0
    case dom::WebGLPowerPreference::High_performance:
539
0
    default:
540
0
        flags |= gl::CreateContextFlags::HIGH_POWER;
541
0
        break;
542
0
    }
543
0
544
#ifdef XP_MACOSX
545
    const nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
546
    nsString vendorID, deviceID;
547
548
    // Avoid crash for Intel HD Graphics 3000 on OSX. (Bug 1413269)
549
    gfxInfo->GetAdapterVendorID(vendorID);
550
    gfxInfo->GetAdapterDeviceID(deviceID);
551
    if (vendorID.EqualsLiteral("0x8086") &&
552
        (deviceID.EqualsLiteral("0x0116") || deviceID.EqualsLiteral("0x0126")))
553
    {
554
        flags |= gl::CreateContextFlags::REQUIRE_COMPAT_PROFILE;
555
    }
556
#endif
557
558
0
    // --
559
0
560
0
    const auto surfaceCaps = [&]() {
561
0
        auto ret = gl::SurfaceCaps::ForRGBA();
562
0
        ret.premultAlpha = mOptions.premultipliedAlpha;
563
0
        ret.preserve = mOptions.preserveDrawingBuffer;
564
0
565
0
        if (!mOptions.alpha) {
566
0
            ret.premultAlpha = true;
567
0
        }
568
0
        return ret;
569
0
    }();
570
0
571
0
    // --
572
0
573
0
    const bool useEGL = PR_GetEnv("MOZ_WEBGL_FORCE_EGL");
574
0
575
#ifdef XP_WIN
576
    tryNativeGL = false;
577
    tryANGLE = true;
578
579
    if (gfxPrefs::WebGLDisableWGL()) {
580
        tryNativeGL = false;
581
    }
582
583
    if (gfxPrefs::WebGLDisableANGLE() || PR_GetEnv("MOZ_WEBGL_FORCE_OPENGL") || useEGL) {
584
        tryNativeGL = true;
585
        tryANGLE = false;
586
    }
587
#endif
588
589
0
    if (tryNativeGL && !forceEnabled) {
590
0
        const nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
591
0
        const auto feature = nsIGfxInfo::FEATURE_WEBGL_OPENGL;
592
0
593
0
        FailureReason reason;
594
0
        if (IsFeatureInBlacklist(gfxInfo, feature, &reason.key)) {
595
0
            reason.info = "Refused to create native OpenGL context because of blacklist"
596
0
                          " entry: ";
597
0
            reason.info.Append(reason.key);
598
0
599
0
            out_failReasons->push_back(reason);
600
0
601
0
            GenerateWarning("%s", reason.info.BeginReading());
602
0
            tryNativeGL = false;
603
0
        }
604
0
    }
605
0
606
0
    // --
607
0
608
0
    typedef decltype(gl::GLContextProviderEGL::CreateOffscreen) fnCreateOffscreenT;
609
0
    const auto fnCreate = [&](fnCreateOffscreenT* const pfnCreateOffscreen,
610
0
                              const char* const info)
611
0
    {
612
0
        const gfx::IntSize dummySize(1, 1);
613
0
        nsCString failureId;
614
0
        const RefPtr<GLContext> gl = pfnCreateOffscreen(dummySize, surfaceCaps, flags,
615
0
                                                        &failureId);
616
0
        if (!gl) {
617
0
            out_failReasons->push_back(WebGLContext::FailureReason(failureId, info));
618
0
        }
619
0
        return gl;
620
0
    };
621
0
622
0
    const auto newGL = [&]() -> RefPtr<gl::GLContext> {
623
0
        if (tryNativeGL) {
624
0
            if (useEGL)
625
0
                return fnCreate(&gl::GLContextProviderEGL::CreateOffscreen, "useEGL");
626
0
627
0
            const auto ret = fnCreate(&gl::GLContextProvider::CreateOffscreen,
628
0
                                      "tryNativeGL");
629
0
            if (ret)
630
0
                return ret;
631
0
        }
632
0
633
0
        if (tryANGLE) {
634
0
            // Force enable alpha channel to make sure ANGLE use correct framebuffer format
635
0
            MOZ_ASSERT(surfaceCaps.alpha);
636
0
            return fnCreate(&gl::GLContextProviderEGL::CreateOffscreen, "tryANGLE");
637
0
        }
638
0
        return nullptr;
639
0
    }();
640
0
641
0
    if (!newGL) {
642
0
        out_failReasons->push_back(FailureReason("FEATURE_FAILURE_WEBGL_EXHAUSTED_DRIVERS",
643
0
                                                 "Exhausted GL driver options."));
644
0
        return false;
645
0
    }
646
0
647
0
    // --
648
0
649
0
    FailureReason reason;
650
0
651
0
    mGL_OnlyClearInDestroyResourcesAndContext = newGL;
652
0
    MOZ_RELEASE_ASSERT(gl);
653
0
    if (!InitAndValidateGL(&reason)) {
654
0
        DestroyResourcesAndContext();
655
0
        MOZ_RELEASE_ASSERT(!gl);
656
0
657
0
        // The fail reason here should be specific enough for now.
658
0
        out_failReasons->push_back(reason);
659
0
        return false;
660
0
    }
661
0
662
0
    return true;
663
0
}
664
665
// Fallback for resizes:
666
667
bool
668
WebGLContext::EnsureDefaultFB()
669
0
{
670
0
    if (mDefaultFB) {
671
0
        MOZ_ASSERT(mDefaultFB->mSize == mRequestedSize);
672
0
        return true;
673
0
    }
674
0
675
0
    const bool depthStencil = mOptions.depth || mOptions.stencil;
676
0
    auto attemptSize = mRequestedSize;
677
0
678
0
    while (attemptSize.width || attemptSize.height) {
679
0
        attemptSize.width = std::max(attemptSize.width, 1);
680
0
        attemptSize.height = std::max(attemptSize.height, 1);
681
0
682
0
        [&]() {
683
0
            if (mOptions.antialias) {
684
0
                MOZ_ASSERT(!mDefaultFB);
685
0
                mDefaultFB = MozFramebuffer::Create(gl, attemptSize, mMsaaSamples,
686
0
                                                    depthStencil);
687
0
                if (mDefaultFB)
688
0
                    return;
689
0
                if (mOptionsFrozen)
690
0
                    return;
691
0
            }
692
0
693
0
            MOZ_ASSERT(!mDefaultFB);
694
0
            mDefaultFB = MozFramebuffer::Create(gl, attemptSize, 0, depthStencil);
695
0
        }();
696
0
697
0
        if (mDefaultFB)
698
0
            break;
699
0
700
0
        attemptSize.width /= 2;
701
0
        attemptSize.height /= 2;
702
0
    }
703
0
704
0
    if (!mDefaultFB) {
705
0
        GenerateWarning("Backbuffer resize failed. Losing context.");
706
0
        ForceLoseContext();
707
0
        return false;
708
0
    }
709
0
710
0
    mDefaultFB_IsInvalid = true;
711
0
712
0
    if (mDefaultFB->mSize != mRequestedSize) {
713
0
        GenerateWarning("Requested size %dx%d was too large, but resize"
714
0
                          " to %dx%d succeeded.",
715
0
                        mRequestedSize.width, mRequestedSize.height,
716
0
                        mDefaultFB->mSize.width, mDefaultFB->mSize.height);
717
0
    }
718
0
    mRequestedSize = mDefaultFB->mSize;
719
0
    return true;
720
0
}
721
722
void
723
WebGLContext::ThrowEvent_WebGLContextCreationError(const nsACString& text)
724
0
{
725
0
    RefPtr<EventTarget> target = mCanvasElement;
726
0
    if (!target && mOffscreenCanvas) {
727
0
        target = mOffscreenCanvas;
728
0
    } else if (!target) {
729
0
        GenerateWarning("Failed to create WebGL context: %s", text.BeginReading());
730
0
        return;
731
0
    }
732
0
733
0
    const auto kEventName = NS_LITERAL_STRING("webglcontextcreationerror");
734
0
735
0
    WebGLContextEventInit eventInit;
736
0
    // eventInit.mCancelable = true; // The spec says this, but it's silly.
737
0
    eventInit.mStatusMessage = NS_ConvertASCIItoUTF16(text);
738
0
739
0
    const RefPtr<WebGLContextEvent> event = WebGLContextEvent::Constructor(target,
740
0
                                                                           kEventName,
741
0
                                                                           eventInit);
742
0
    event->SetTrusted(true);
743
0
744
0
    target->DispatchEvent(*event);
745
0
746
0
    //////
747
0
748
0
    GenerateWarning("Failed to create WebGL context: %s", text.BeginReading());
749
0
}
750
751
NS_IMETHODIMP
752
WebGLContext::SetDimensions(int32_t signedWidth, int32_t signedHeight)
753
0
{
754
0
    const FuncScope funcScope(*this, "<SetDimensions>");
755
0
    (void)IsContextLost(); // We handle this ourselves.
756
0
757
0
    if (signedWidth < 0 || signedHeight < 0) {
758
0
        if (!gl) {
759
0
            Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_FAILURE_ID,
760
0
                                  NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBGL_SIZE"));
761
0
        }
762
0
        GenerateWarning("Canvas size is too large (seems like a negative value wrapped)");
763
0
        return NS_ERROR_OUT_OF_MEMORY;
764
0
    }
765
0
766
0
    uint32_t width = signedWidth;
767
0
    uint32_t height = signedHeight;
768
0
769
0
    // Early success return cases
770
0
771
0
    // May have a OffscreenCanvas instead of an HTMLCanvasElement
772
0
    if (GetCanvas())
773
0
        GetCanvas()->InvalidateCanvas();
774
0
775
0
    // Zero-sized surfaces can cause problems.
776
0
    if (width == 0)
777
0
        width = 1;
778
0
779
0
    if (height == 0)
780
0
        height = 1;
781
0
782
0
    // If we already have a gl context, then we just need to resize it
783
0
    if (gl) {
784
0
        if (uint32_t(mRequestedSize.width) == width &&
785
0
            uint32_t(mRequestedSize.height) == height)
786
0
        {
787
0
            return NS_OK;
788
0
        }
789
0
790
0
        if (IsContextLost())
791
0
            return NS_OK;
792
0
793
0
        // If we've already drawn, we should commit the current buffer.
794
0
        PresentScreenBuffer(gl->Screen());
795
0
796
0
        if (IsContextLost()) {
797
0
            GenerateWarning("WebGL context was lost due to swap failure.");
798
0
            return NS_OK;
799
0
        }
800
0
801
0
        // Kill our current default fb(s), for later lazy allocation.
802
0
        mRequestedSize = {width, height};
803
0
        mDefaultFB = nullptr;
804
0
805
0
        mResetLayer = true;
806
0
        return NS_OK;
807
0
    }
808
0
809
0
    nsCString failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBGL_UNKOWN");
810
0
    auto autoTelemetry = mozilla::MakeScopeExit([&] {
811
0
        Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_FAILURE_ID,
812
0
                              failureId);
813
0
    });
814
0
815
0
    // End of early return cases.
816
0
    // At this point we know that we're not just resizing an existing context,
817
0
    // we are initializing a new context.
818
0
819
0
    // if we exceeded either the global or the per-principal limit for WebGL contexts,
820
0
    // lose the oldest-used context now to free resources. Note that we can't do that
821
0
    // in the WebGLContext constructor as we don't have a canvas element yet there.
822
0
    // Here is the right place to do so, as we are about to create the OpenGL context
823
0
    // and that is what can fail if we already have too many.
824
0
    LoseOldestWebGLContextIfLimitExceeded();
825
0
826
0
    // We're going to create an entirely new context.  If our
827
0
    // generation is not 0 right now (that is, if this isn't the first
828
0
    // context we're creating), we may have to dispatch a context lost
829
0
    // event.
830
0
831
0
    // If incrementing the generation would cause overflow,
832
0
    // don't allow it.  Allowing this would allow us to use
833
0
    // resource handles created from older context generations.
834
0
    if (!(mGeneration + 1).isValid()) {
835
0
        // exit without changing the value of mGeneration
836
0
        failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBGL_TOO_MANY");
837
0
        const nsLiteralCString text("Too many WebGL contexts created this run.");
838
0
        ThrowEvent_WebGLContextCreationError(text);
839
0
        return NS_ERROR_FAILURE;
840
0
    }
841
0
842
0
    // increment the generation number - Do this early because later
843
0
    // in CreateOffscreenGL(), "default" objects are created that will
844
0
    // pick up the old generation.
845
0
    ++mGeneration;
846
0
847
0
    bool disabled = gfxPrefs::WebGLDisabled();
848
0
849
0
    // TODO: When we have software webgl support we should use that instead.
850
0
    disabled |= gfxPlatform::InSafeMode();
851
0
852
0
    if (disabled) {
853
0
        if (gfxPlatform::InSafeMode()) {
854
0
            failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBGL_SAFEMODE");
855
0
        } else {
856
0
            failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBGL_DISABLED");
857
0
        }
858
0
        const nsLiteralCString text("WebGL is currently disabled.");
859
0
        ThrowEvent_WebGLContextCreationError(text);
860
0
        return NS_ERROR_FAILURE;
861
0
    }
862
0
863
0
    if (gfxPrefs::WebGLDisableFailIfMajorPerformanceCaveat()) {
864
0
        mOptions.failIfMajorPerformanceCaveat = false;
865
0
    }
866
0
867
0
    if (mOptions.failIfMajorPerformanceCaveat) {
868
0
        nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
869
0
        if (!HasAcceleratedLayers(gfxInfo)) {
870
0
            failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBGL_PERF_CAVEAT");
871
0
            const nsLiteralCString text("failIfMajorPerformanceCaveat: Compositor is not"
872
0
                                        " hardware-accelerated.");
873
0
            ThrowEvent_WebGLContextCreationError(text);
874
0
            return NS_ERROR_FAILURE;
875
0
        }
876
0
    }
877
0
878
0
    // Alright, now let's start trying.
879
0
    bool forceEnabled = gfxPrefs::WebGLForceEnabled();
880
0
    ScopedGfxFeatureReporter reporter("WebGL", forceEnabled);
881
0
882
0
    MOZ_ASSERT(!gl);
883
0
    std::vector<FailureReason> failReasons;
884
0
    if (!CreateAndInitGL(forceEnabled, &failReasons)) {
885
0
        nsCString text("WebGL creation failed: ");
886
0
        for (const auto& cur : failReasons) {
887
0
            // Don't try to accumulate using an empty key if |cur.key| is empty.
888
0
            if (cur.key.IsEmpty()) {
889
0
                Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_FAILURE_ID,
890
0
                                      NS_LITERAL_CSTRING("FEATURE_FAILURE_REASON_UNKNOWN"));
891
0
            } else {
892
0
                Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_FAILURE_ID, cur.key);
893
0
            }
894
0
895
0
            text.AppendLiteral("\n* ");
896
0
            text.Append(cur.info);
897
0
        }
898
0
        failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_REASON");
899
0
        ThrowEvent_WebGLContextCreationError(text);
900
0
        return NS_ERROR_FAILURE;
901
0
    }
902
0
    MOZ_ASSERT(gl);
903
0
904
0
    if (mOptions.failIfMajorPerformanceCaveat) {
905
0
        if (gl->IsWARP()) {
906
0
            DestroyResourcesAndContext();
907
0
            MOZ_ASSERT(!gl);
908
0
909
0
            failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBGL_PERF_WARP");
910
0
            const nsLiteralCString text("failIfMajorPerformanceCaveat: Driver is not"
911
0
                                        " hardware-accelerated.");
912
0
            ThrowEvent_WebGLContextCreationError(text);
913
0
            return NS_ERROR_FAILURE;
914
0
        }
915
0
916
#ifdef XP_WIN
917
        if (gl->GetContextType() == gl::GLContextType::WGL &&
918
            !gl::sWGLLib.HasDXInterop2())
919
        {
920
            DestroyResourcesAndContext();
921
            MOZ_ASSERT(!gl);
922
923
            failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBGL_DXGL_INTEROP2");
924
            const nsLiteralCString text("Caveat: WGL without DXGLInterop2.");
925
            ThrowEvent_WebGLContextCreationError(text);
926
            return NS_ERROR_FAILURE;
927
        }
928
#endif
929
    }
930
0
931
0
    MOZ_ASSERT(!mDefaultFB);
932
0
    mRequestedSize = {width, height};
933
0
    if (!EnsureDefaultFB()) {
934
0
        MOZ_ASSERT(!gl);
935
0
936
0
        failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBGL_BACKBUFFER");
937
0
        const nsLiteralCString text("Initializing WebGL backbuffer failed.");
938
0
        ThrowEvent_WebGLContextCreationError(text);
939
0
        return NS_ERROR_FAILURE;
940
0
    }
941
0
942
0
    if (GLContext::ShouldSpew()) {
943
0
        printf_stderr("--- WebGL context created: %p\n", gl.get());
944
0
    }
945
0
946
0
    // Update our internal stuff:
947
0
948
0
    mOptions.antialias &= bool(mDefaultFB->mSamples);
949
0
950
0
    if (!mOptions.alpha) {
951
0
        // We always have alpha.
952
0
        mNeedsFakeNoAlpha = true;
953
0
    }
954
0
955
0
    if (mOptions.depth || mOptions.stencil) {
956
0
        // We always have depth+stencil if we have either.
957
0
        if (!mOptions.depth) {
958
0
            mNeedsFakeNoDepth = true;
959
0
        }
960
0
        if (!mOptions.stencil) {
961
0
            mNeedsFakeNoStencil = true;
962
0
        }
963
0
    }
964
0
965
0
    mNeedsFakeNoStencil_UserFBs = false;
966
#ifdef MOZ_WIDGET_COCOA
967
    if (!nsCocoaFeatures::IsAtLeastVersion(10, 12) &&
968
        gl->Vendor() == GLVendor::Intel)
969
    {
970
        mNeedsFakeNoStencil_UserFBs = true;
971
    }
972
#endif
973
974
0
    mResetLayer = true;
975
0
    mOptionsFrozen = true;
976
0
977
0
    //////
978
0
    // Initial setup.
979
0
980
0
    gl->mImplicitMakeCurrent = true;
981
0
982
0
    const auto& size = mDefaultFB->mSize;
983
0
984
0
    mViewportX = mViewportY = 0;
985
0
    mViewportWidth = size.width;
986
0
    mViewportHeight = size.height;
987
0
    gl->fViewport(mViewportX, mViewportY, mViewportWidth, mViewportHeight);
988
0
989
0
    gl->fScissor(0, 0, size.width, size.height);
990
0
991
0
    //////
992
0
    // Check everything
993
0
994
0
    AssertCachedBindings();
995
0
    AssertCachedGlobalState();
996
0
997
0
    mShouldPresent = true;
998
0
999
0
    //////
1000
0
1001
0
    reporter.SetSuccessful();
1002
0
1003
0
    failureId = NS_LITERAL_CSTRING("SUCCESS");
1004
0
1005
0
    gl->ResetSyncCallCount("WebGLContext Initialization");
1006
0
    return NS_OK;
1007
0
}
1008
1009
void
1010
WebGLContext::LoseOldestWebGLContextIfLimitExceeded()
1011
0
{
1012
0
    const auto maxWebGLContexts = gfxPrefs::WebGLMaxContexts();
1013
0
    auto maxWebGLContextsPerPrincipal = gfxPrefs::WebGLMaxContextsPerPrincipal();
1014
0
1015
0
    // maxWebGLContextsPerPrincipal must be less than maxWebGLContexts
1016
0
    MOZ_ASSERT(maxWebGLContextsPerPrincipal <= maxWebGLContexts);
1017
0
    maxWebGLContextsPerPrincipal = std::min(maxWebGLContextsPerPrincipal, maxWebGLContexts);
1018
0
1019
0
    if (!NS_IsMainThread()) {
1020
0
        // XXX mtseng: bug 709490, WebGLMemoryTracker is not thread safe.
1021
0
        return;
1022
0
    }
1023
0
1024
0
    // it's important to update the index on a new context before losing old contexts,
1025
0
    // otherwise new unused contexts would all have index 0 and we couldn't distinguish older ones
1026
0
    // when choosing which one to lose first.
1027
0
    UpdateLastUseIndex();
1028
0
1029
0
    WebGLMemoryTracker::ContextsArrayType& contexts = WebGLMemoryTracker::Contexts();
1030
0
1031
0
    // quick exit path, should cover a majority of cases
1032
0
    if (contexts.Length() <= maxWebGLContextsPerPrincipal)
1033
0
        return;
1034
0
1035
0
    // note that here by "context" we mean "non-lost context". See the check for
1036
0
    // IsContextLost() below. Indeed, the point of this function is to maybe lose
1037
0
    // some currently non-lost context.
1038
0
1039
0
    uint64_t oldestIndex = UINT64_MAX;
1040
0
    uint64_t oldestIndexThisPrincipal = UINT64_MAX;
1041
0
    const WebGLContext* oldestContext = nullptr;
1042
0
    const WebGLContext* oldestContextThisPrincipal = nullptr;
1043
0
    size_t numContexts = 0;
1044
0
    size_t numContextsThisPrincipal = 0;
1045
0
1046
0
    for(size_t i = 0; i < contexts.Length(); ++i) {
1047
0
        // don't want to lose ourselves.
1048
0
        if (contexts[i] == this)
1049
0
            continue;
1050
0
1051
0
        if (!contexts[i]->gl)
1052
0
            continue;
1053
0
1054
0
        if (!contexts[i]->GetCanvas()) {
1055
0
            // Zombie context: the canvas is already destroyed, but something else
1056
0
            // (typically the compositor) is still holding on to the context.
1057
0
            // Killing zombies is a no-brainer.
1058
0
            const_cast<WebGLContext*>(contexts[i])->LoseContext();
1059
0
            continue;
1060
0
        }
1061
0
1062
0
        numContexts++;
1063
0
        if (contexts[i]->mLastUseIndex < oldestIndex) {
1064
0
            oldestIndex = contexts[i]->mLastUseIndex;
1065
0
            oldestContext = contexts[i];
1066
0
        }
1067
0
1068
0
        nsIPrincipal* ourPrincipal = GetCanvas()->NodePrincipal();
1069
0
        nsIPrincipal* theirPrincipal = contexts[i]->GetCanvas()->NodePrincipal();
1070
0
        bool samePrincipal;
1071
0
        nsresult rv = ourPrincipal->Equals(theirPrincipal, &samePrincipal);
1072
0
        if (NS_SUCCEEDED(rv) && samePrincipal) {
1073
0
            numContextsThisPrincipal++;
1074
0
            if (contexts[i]->mLastUseIndex < oldestIndexThisPrincipal) {
1075
0
                oldestIndexThisPrincipal = contexts[i]->mLastUseIndex;
1076
0
                oldestContextThisPrincipal = contexts[i];
1077
0
            }
1078
0
        }
1079
0
    }
1080
0
1081
0
    if (numContextsThisPrincipal > maxWebGLContextsPerPrincipal) {
1082
0
        GenerateWarning("Exceeded %u live WebGL contexts for this principal, losing the "
1083
0
                        "least recently used one.", maxWebGLContextsPerPrincipal);
1084
0
        MOZ_ASSERT(oldestContextThisPrincipal); // if we reach this point, this can't be null
1085
0
        const_cast<WebGLContext*>(oldestContextThisPrincipal)->LoseContext();
1086
0
    } else if (numContexts > maxWebGLContexts) {
1087
0
        GenerateWarning("Exceeded %u live WebGL contexts, losing the least "
1088
0
                        "recently used one.", maxWebGLContexts);
1089
0
        MOZ_ASSERT(oldestContext); // if we reach this point, this can't be null
1090
0
        const_cast<WebGLContext*>(oldestContext)->LoseContext();
1091
0
    }
1092
0
}
1093
1094
UniquePtr<uint8_t[]>
1095
WebGLContext::GetImageBuffer(int32_t* out_format)
1096
0
{
1097
0
    *out_format = 0;
1098
0
1099
0
    // Use GetSurfaceSnapshot() to make sure that appropriate y-flip gets applied
1100
0
    gfxAlphaType any;
1101
0
    RefPtr<SourceSurface> snapshot = GetSurfaceSnapshot(&any);
1102
0
    if (!snapshot)
1103
0
        return nullptr;
1104
0
1105
0
    RefPtr<DataSourceSurface> dataSurface = snapshot->GetDataSurface();
1106
0
1107
0
    return gfxUtils::GetImageBuffer(dataSurface, mOptions.premultipliedAlpha,
1108
0
                                    out_format);
1109
0
}
1110
1111
NS_IMETHODIMP
1112
WebGLContext::GetInputStream(const char* mimeType,
1113
                             const char16_t* encoderOptions,
1114
                             nsIInputStream** out_stream)
1115
0
{
1116
0
    NS_ASSERTION(gl, "GetInputStream on invalid context?");
1117
0
    if (!gl)
1118
0
        return NS_ERROR_FAILURE;
1119
0
1120
0
    // Use GetSurfaceSnapshot() to make sure that appropriate y-flip gets applied
1121
0
    gfxAlphaType any;
1122
0
    RefPtr<SourceSurface> snapshot = GetSurfaceSnapshot(&any);
1123
0
    if (!snapshot)
1124
0
        return NS_ERROR_FAILURE;
1125
0
1126
0
    RefPtr<DataSourceSurface> dataSurface = snapshot->GetDataSurface();
1127
0
    return gfxUtils::GetInputStream(dataSurface, mOptions.premultipliedAlpha, mimeType,
1128
0
                                    encoderOptions, out_stream);
1129
0
}
1130
1131
void
1132
WebGLContext::UpdateLastUseIndex()
1133
0
{
1134
0
    static CheckedInt<uint64_t> sIndex = 0;
1135
0
1136
0
    sIndex++;
1137
0
1138
0
    // should never happen with 64-bit; trying to handle this would be riskier than
1139
0
    // not handling it as the handler code would never get exercised.
1140
0
    if (!sIndex.isValid())
1141
0
        MOZ_CRASH("Can't believe it's been 2^64 transactions already!");
1142
0
    mLastUseIndex = sIndex.value();
1143
0
}
1144
1145
static uint8_t gWebGLLayerUserData;
1146
1147
class WebGLContextUserData : public LayerUserData
1148
{
1149
public:
1150
    explicit WebGLContextUserData(HTMLCanvasElement* canvas)
1151
        : mCanvas(canvas)
1152
0
    {}
1153
1154
    /* PreTransactionCallback gets called by the Layers code every time the
1155
     * WebGL canvas is going to be composited.
1156
     */
1157
0
    static void PreTransactionCallback(void* data) {
1158
0
        WebGLContext* webgl = static_cast<WebGLContext*>(data);
1159
0
1160
0
        // Prepare the context for composition
1161
0
        webgl->BeginComposition();
1162
0
    }
1163
1164
    /** DidTransactionCallback gets called by the Layers code everytime the WebGL canvas gets composite,
1165
      * so it really is the right place to put actions that have to be performed upon compositing
1166
      */
1167
0
    static void DidTransactionCallback(void* data) {
1168
0
        WebGLContext* webgl = static_cast<WebGLContext*>(data);
1169
0
1170
0
        // Clean up the context after composition
1171
0
        webgl->EndComposition();
1172
0
    }
1173
1174
private:
1175
    RefPtr<HTMLCanvasElement> mCanvas;
1176
};
1177
1178
already_AddRefed<layers::Layer>
1179
WebGLContext::GetCanvasLayer(nsDisplayListBuilder* builder,
1180
                             Layer* oldLayer,
1181
                             LayerManager* manager)
1182
0
{
1183
0
    if (!mResetLayer && oldLayer &&
1184
0
        oldLayer->HasUserData(&gWebGLLayerUserData))
1185
0
    {
1186
0
        RefPtr<layers::Layer> ret = oldLayer;
1187
0
        return ret.forget();
1188
0
    }
1189
0
1190
0
    RefPtr<CanvasLayer> canvasLayer = manager->CreateCanvasLayer();
1191
0
    if (!canvasLayer) {
1192
0
        NS_WARNING("CreateCanvasLayer returned null!");
1193
0
        return nullptr;
1194
0
    }
1195
0
1196
0
    WebGLContextUserData* userData = nullptr;
1197
0
    if (builder->IsPaintingToWindow() && mCanvasElement) {
1198
0
        userData = new WebGLContextUserData(mCanvasElement);
1199
0
    }
1200
0
1201
0
    canvasLayer->SetUserData(&gWebGLLayerUserData, userData);
1202
0
1203
0
    CanvasRenderer* canvasRenderer = canvasLayer->CreateOrGetCanvasRenderer();
1204
0
    if (!InitializeCanvasRenderer(builder, canvasRenderer))
1205
0
      return nullptr;
1206
0
1207
0
    uint32_t flags = gl->Caps().alpha ? 0 : Layer::CONTENT_OPAQUE;
1208
0
    canvasLayer->SetContentFlags(flags);
1209
0
1210
0
    mResetLayer = false;
1211
0
1212
0
    return canvasLayer.forget();
1213
0
}
1214
1215
bool
1216
WebGLContext::UpdateWebRenderCanvasData(nsDisplayListBuilder* aBuilder,
1217
                                        WebRenderCanvasData* aCanvasData)
1218
0
{
1219
0
  CanvasRenderer* renderer = aCanvasData->GetCanvasRenderer();
1220
0
1221
0
  if(!mResetLayer && renderer) {
1222
0
    return true;
1223
0
  }
1224
0
1225
0
  renderer = aCanvasData->CreateCanvasRenderer();
1226
0
  if (!InitializeCanvasRenderer(aBuilder, renderer)) {
1227
0
    // Clear CanvasRenderer of WebRenderCanvasData
1228
0
    aCanvasData->ClearCanvasRenderer();
1229
0
    return false;
1230
0
  }
1231
0
1232
0
  MOZ_ASSERT(renderer);
1233
0
  mResetLayer = false;
1234
0
  return true;
1235
0
}
1236
1237
bool
1238
WebGLContext::InitializeCanvasRenderer(nsDisplayListBuilder* aBuilder,
1239
                                       CanvasRenderer* aRenderer)
1240
0
{
1241
0
    const FuncScope funcScope(*this, "<InitializeCanvasRenderer>");
1242
0
    if (IsContextLost())
1243
0
        return false;
1244
0
1245
0
    CanvasInitializeData data;
1246
0
    if (aBuilder->IsPaintingToWindow() && mCanvasElement) {
1247
0
        // Make the layer tell us whenever a transaction finishes (including
1248
0
        // the current transaction), so we can clear our invalidation state and
1249
0
        // start invalidating again. We need to do this for the layer that is
1250
0
        // being painted to a window (there shouldn't be more than one at a time,
1251
0
        // and if there is, flushing the invalidation state more often than
1252
0
        // necessary is harmless).
1253
0
1254
0
        // The layer will be destroyed when we tear down the presentation
1255
0
        // (at the latest), at which time this userData will be destroyed,
1256
0
        // releasing the reference to the element.
1257
0
        // The userData will receive DidTransactionCallbacks, which flush the
1258
0
        // the invalidation state to indicate that the canvas is up to date.
1259
0
        data.mPreTransCallback = WebGLContextUserData::PreTransactionCallback;
1260
0
        data.mPreTransCallbackData = this;
1261
0
        data.mDidTransCallback = WebGLContextUserData::DidTransactionCallback;
1262
0
        data.mDidTransCallbackData = this;
1263
0
    }
1264
0
1265
0
    data.mGLContext = gl;
1266
0
    data.mSize = DrawingBufferSize();
1267
0
    data.mHasAlpha = mOptions.alpha;
1268
0
    data.mIsGLAlphaPremult = IsPremultAlpha() || !data.mHasAlpha;
1269
0
1270
0
    aRenderer->Initialize(data);
1271
0
    aRenderer->SetDirty();
1272
0
    return true;
1273
0
}
1274
1275
layers::LayersBackend
1276
WebGLContext::GetCompositorBackendType() const
1277
0
{
1278
0
    if (mCanvasElement) {
1279
0
        return mCanvasElement->GetCompositorBackendType();
1280
0
    } else if (mOffscreenCanvas) {
1281
0
        return mOffscreenCanvas->GetCompositorBackendType();
1282
0
    }
1283
0
1284
0
    return LayersBackend::LAYERS_NONE;
1285
0
}
1286
1287
nsIDocument*
1288
WebGLContext::GetOwnerDoc() const
1289
0
{
1290
0
    MOZ_ASSERT(mCanvasElement);
1291
0
    if (!mCanvasElement) {
1292
0
        return nullptr;
1293
0
    }
1294
0
    return mCanvasElement->OwnerDoc();
1295
0
}
1296
1297
void
1298
WebGLContext::Commit()
1299
0
{
1300
0
    if (mOffscreenCanvas) {
1301
0
        mOffscreenCanvas->CommitFrameToCompositor();
1302
0
    }
1303
0
}
1304
1305
void
1306
WebGLContext::GetCanvas(Nullable<dom::OwningHTMLCanvasElementOrOffscreenCanvas>& retval)
1307
0
{
1308
0
    if (mCanvasElement) {
1309
0
        MOZ_RELEASE_ASSERT(!mOffscreenCanvas, "GFX: Canvas is offscreen.");
1310
0
1311
0
        if (mCanvasElement->IsInNativeAnonymousSubtree()) {
1312
0
          retval.SetNull();
1313
0
        } else {
1314
0
          retval.SetValue().SetAsHTMLCanvasElement() = mCanvasElement;
1315
0
        }
1316
0
    } else if (mOffscreenCanvas) {
1317
0
        retval.SetValue().SetAsOffscreenCanvas() = mOffscreenCanvas;
1318
0
    } else {
1319
0
        retval.SetNull();
1320
0
    }
1321
0
}
1322
1323
void
1324
WebGLContext::GetContextAttributes(dom::Nullable<dom::WebGLContextAttributes>& retval)
1325
0
{
1326
0
    retval.SetNull();
1327
0
    const FuncScope funcScope(*this, "getContextAttributes");
1328
0
    if (IsContextLost())
1329
0
        return;
1330
0
1331
0
    dom::WebGLContextAttributes& result = retval.SetValue();
1332
0
1333
0
    result.mAlpha.Construct(mOptions.alpha);
1334
0
    result.mDepth = mOptions.depth;
1335
0
    result.mStencil = mOptions.stencil;
1336
0
    result.mAntialias = mOptions.antialias;
1337
0
    result.mPremultipliedAlpha = mOptions.premultipliedAlpha;
1338
0
    result.mPreserveDrawingBuffer = mOptions.preserveDrawingBuffer;
1339
0
    result.mFailIfMajorPerformanceCaveat = mOptions.failIfMajorPerformanceCaveat;
1340
0
    result.mPowerPreference = mOptions.powerPreference;
1341
0
}
1342
1343
void
1344
WebGLContext::ForceClearFramebufferWithDefaultValues(const GLbitfield clearBits,
1345
                                                     const bool fakeNoAlpha) const
1346
0
{
1347
0
    const bool initializeColorBuffer = bool(clearBits & LOCAL_GL_COLOR_BUFFER_BIT);
1348
0
    const bool initializeDepthBuffer = bool(clearBits & LOCAL_GL_DEPTH_BUFFER_BIT);
1349
0
    const bool initializeStencilBuffer = bool(clearBits & LOCAL_GL_STENCIL_BUFFER_BIT);
1350
0
1351
0
    // Fun GL fact: No need to worry about the viewport here, glViewport is just
1352
0
    // setting up a coordinates transformation, it doesn't affect glClear at all.
1353
0
    AssertCachedGlobalState();
1354
0
1355
0
    // Prepare GL state for clearing.
1356
0
    if (mScissorTestEnabled) {
1357
0
        gl->fDisable(LOCAL_GL_SCISSOR_TEST);
1358
0
    }
1359
0
1360
0
    if (initializeColorBuffer) {
1361
0
        DoColorMask(0x0f);
1362
0
1363
0
        if (fakeNoAlpha) {
1364
0
            gl->fClearColor(0.0f, 0.0f, 0.0f, 1.0f);
1365
0
        } else {
1366
0
            gl->fClearColor(0.0f, 0.0f, 0.0f, 0.0f);
1367
0
        }
1368
0
    }
1369
0
1370
0
    if (initializeDepthBuffer) {
1371
0
        gl->fDepthMask(1);
1372
0
        gl->fClearDepth(1.0f);
1373
0
    }
1374
0
1375
0
    if (initializeStencilBuffer) {
1376
0
        // "The clear operation always uses the front stencil write mask
1377
0
        //  when clearing the stencil buffer."
1378
0
        gl->fStencilMaskSeparate(LOCAL_GL_FRONT, 0xffffffff);
1379
0
        gl->fStencilMaskSeparate(LOCAL_GL_BACK,  0xffffffff);
1380
0
        gl->fClearStencil(0);
1381
0
    }
1382
0
1383
0
    if (mRasterizerDiscardEnabled) {
1384
0
        gl->fDisable(LOCAL_GL_RASTERIZER_DISCARD);
1385
0
    }
1386
0
1387
0
    // Do the clear!
1388
0
    gl->fClear(clearBits);
1389
0
1390
0
    // And reset!
1391
0
    if (mScissorTestEnabled) {
1392
0
        gl->fEnable(LOCAL_GL_SCISSOR_TEST);
1393
0
    }
1394
0
1395
0
    if (mRasterizerDiscardEnabled) {
1396
0
        gl->fEnable(LOCAL_GL_RASTERIZER_DISCARD);
1397
0
    }
1398
0
1399
0
    // Restore GL state after clearing.
1400
0
    if (initializeColorBuffer) {
1401
0
        gl->fClearColor(mColorClearValue[0],
1402
0
                        mColorClearValue[1],
1403
0
                        mColorClearValue[2],
1404
0
                        mColorClearValue[3]);
1405
0
    }
1406
0
1407
0
    if (initializeDepthBuffer) {
1408
0
        gl->fDepthMask(mDepthWriteMask);
1409
0
        gl->fClearDepth(mDepthClearValue);
1410
0
    }
1411
0
1412
0
    if (initializeStencilBuffer) {
1413
0
        gl->fStencilMaskSeparate(LOCAL_GL_FRONT, mStencilWriteMaskFront);
1414
0
        gl->fStencilMaskSeparate(LOCAL_GL_BACK,  mStencilWriteMaskBack);
1415
0
        gl->fClearStencil(mStencilClearValue);
1416
0
    }
1417
0
}
1418
1419
void
1420
WebGLContext::OnEndOfFrame() const
1421
0
{
1422
0
   if (gfxPrefs::WebGLSpewFrameAllocs()) {
1423
0
      GeneratePerfWarning("[webgl.perf.spew-frame-allocs] %" PRIu64 " data allocations this frame.",
1424
0
                           mDataAllocGLCallCount);
1425
0
   }
1426
0
   mDataAllocGLCallCount = 0;
1427
0
   gl->ResetSyncCallCount("WebGLContext PresentScreenBuffer");
1428
0
}
1429
1430
void
1431
WebGLContext::BlitBackbufferToCurDriverFB() const
1432
0
{
1433
0
    DoColorMask(0x0f);
1434
0
1435
0
    if (mScissorTestEnabled) {
1436
0
        gl->fDisable(LOCAL_GL_SCISSOR_TEST);
1437
0
    }
1438
0
1439
0
    [&]() {
1440
0
        const auto& size = mDefaultFB->mSize;
1441
0
1442
0
        if (gl->IsSupported(GLFeature::framebuffer_blit)) {
1443
0
            gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, mDefaultFB->mFB);
1444
0
            gl->fBlitFramebuffer(0, 0, size.width, size.height,
1445
0
                                 0, 0, size.width, size.height,
1446
0
                                 LOCAL_GL_COLOR_BUFFER_BIT, LOCAL_GL_NEAREST);
1447
0
            return;
1448
0
        }
1449
0
        if (mDefaultFB->mSamples &&
1450
0
            gl->IsExtensionSupported(GLContext::APPLE_framebuffer_multisample))
1451
0
        {
1452
0
            gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, mDefaultFB->mFB);
1453
0
            gl->fResolveMultisampleFramebufferAPPLE();
1454
0
            return;
1455
0
        }
1456
0
1457
0
        gl->BlitHelper()->DrawBlitTextureToFramebuffer(mDefaultFB->ColorTex(), size,
1458
0
                                                       size);
1459
0
    }();
1460
0
1461
0
    if (mScissorTestEnabled) {
1462
0
        gl->fEnable(LOCAL_GL_SCISSOR_TEST);
1463
0
    }
1464
0
}
1465
1466
// For an overview of how WebGL compositing works, see:
1467
// https://wiki.mozilla.org/Platform/GFX/WebGL/Compositing
1468
bool
1469
WebGLContext::PresentScreenBuffer(GLScreenBuffer* const targetScreen)
1470
0
{
1471
0
    const FuncScope funcScope(*this, "<PresentScreenBuffer>");
1472
0
    if (IsContextLost())
1473
0
        return false;
1474
0
1475
0
    if (!mShouldPresent)
1476
0
        return false;
1477
0
1478
0
    if (!ValidateAndInitFB(nullptr))
1479
0
        return false;
1480
0
1481
0
    const auto& screen = targetScreen ? targetScreen : gl->Screen();
1482
0
    if ((!screen->IsReadBufferReady() || screen->Size() != mDefaultFB->mSize) &&
1483
0
        !screen->Resize(mDefaultFB->mSize))
1484
0
    {
1485
0
        GenerateWarning("screen->Resize failed. Losing context.");
1486
0
        ForceLoseContext();
1487
0
        return false;
1488
0
    }
1489
0
1490
0
    gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
1491
0
    BlitBackbufferToCurDriverFB();
1492
0
1493
#ifdef DEBUG
1494
    if (!mOptions.alpha) {
1495
        gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
1496
        uint32_t pixel = 3;
1497
        gl->fReadPixels(0, 0, 1, 1, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, &pixel);
1498
        MOZ_ASSERT((pixel & 0xff000000) == 0xff000000);
1499
    }
1500
#endif
1501
1502
0
    if (!screen->PublishFrame(screen->Size())) {
1503
0
        GenerateWarning("PublishFrame failed. Losing context.");
1504
0
        ForceLoseContext();
1505
0
        return false;
1506
0
    }
1507
0
1508
0
    if (!mOptions.preserveDrawingBuffer) {
1509
0
        if (gl->IsSupported(gl::GLFeature::invalidate_framebuffer)) {
1510
0
            gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mDefaultFB->mFB);
1511
0
            const GLenum attachments[] = { LOCAL_GL_COLOR_ATTACHMENT0 };
1512
0
            gl->fInvalidateFramebuffer(LOCAL_GL_FRAMEBUFFER, 1, attachments);
1513
0
        }
1514
0
        mDefaultFB_IsInvalid = true;
1515
0
    }
1516
0
    mResolvedDefaultFB = nullptr;
1517
0
1518
0
    mShouldPresent = false;
1519
0
    OnEndOfFrame();
1520
0
1521
0
    return true;
1522
0
}
1523
1524
// Prepare the context for capture before compositing
1525
void
1526
WebGLContext::BeginComposition(GLScreenBuffer* const screen)
1527
0
{
1528
0
    // Present our screenbuffer, if needed.
1529
0
    PresentScreenBuffer(screen);
1530
0
    mDrawCallsSinceLastFlush = 0;
1531
0
}
1532
1533
// Clean up the context after captured for compositing
1534
void
1535
WebGLContext::EndComposition()
1536
0
{
1537
0
    // Mark ourselves as no longer invalidated.
1538
0
    MarkContextClean();
1539
0
    UpdateLastUseIndex();
1540
0
}
1541
1542
void
1543
WebGLContext::DummyReadFramebufferOperation()
1544
0
{
1545
0
    if (!mBoundReadFramebuffer)
1546
0
        return; // Infallible.
1547
0
1548
0
    const auto status = mBoundReadFramebuffer->CheckFramebufferStatus();
1549
0
    if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
1550
0
        ErrorInvalidFramebufferOperation("Framebuffer must be complete.");
1551
0
    }
1552
0
}
1553
1554
bool
1555
WebGLContext::Has64BitTimestamps() const
1556
0
{
1557
0
    // 'sync' provides glGetInteger64v either by supporting ARB_sync, GL3+, or GLES3+.
1558
0
    return gl->IsSupported(GLFeature::sync);
1559
0
}
1560
1561
static bool
1562
CheckContextLost(GLContext* gl, bool* const out_isGuilty)
1563
0
{
1564
0
    MOZ_ASSERT(gl);
1565
0
    MOZ_ASSERT(out_isGuilty);
1566
0
1567
0
    bool isEGL = gl->GetContextType() == gl::GLContextType::EGL;
1568
0
1569
0
    GLenum resetStatus = LOCAL_GL_NO_ERROR;
1570
0
    if (gl->IsSupported(GLFeature::robustness)) {
1571
0
        gl->MakeCurrent();
1572
0
        resetStatus = gl->fGetGraphicsResetStatus();
1573
0
    } else if (isEGL) {
1574
0
        // Simulate a ARB_robustness guilty context loss for when we
1575
0
        // get an EGL_CONTEXT_LOST error. It may not actually be guilty,
1576
0
        // but we can't make any distinction.
1577
0
        if (!gl->MakeCurrent(true) && gl->IsContextLost()) {
1578
0
            resetStatus = LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB;
1579
0
        }
1580
0
    }
1581
0
1582
0
    if (resetStatus == LOCAL_GL_NO_ERROR) {
1583
0
        *out_isGuilty = false;
1584
0
        return false;
1585
0
    }
1586
0
1587
0
    // Assume guilty unless we find otherwise!
1588
0
    bool isGuilty = true;
1589
0
    switch (resetStatus) {
1590
0
    case LOCAL_GL_INNOCENT_CONTEXT_RESET_ARB:
1591
0
        // Either nothing wrong, or not our fault.
1592
0
        isGuilty = false;
1593
0
        break;
1594
0
    case LOCAL_GL_GUILTY_CONTEXT_RESET_ARB:
1595
0
        NS_WARNING("WebGL content on the page definitely caused the graphics"
1596
0
                   " card to reset.");
1597
0
        break;
1598
0
    case LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB:
1599
0
        NS_WARNING("WebGL content on the page might have caused the graphics"
1600
0
                   " card to reset");
1601
0
        // If we can't tell, assume guilty.
1602
0
        break;
1603
0
    default:
1604
0
        MOZ_ASSERT(false, "Unreachable.");
1605
0
        // If we do get here, let's pretend to be guilty as an escape plan.
1606
0
        break;
1607
0
    }
1608
0
1609
0
    if (isGuilty) {
1610
0
        NS_WARNING("WebGL context on this page is considered guilty, and will"
1611
0
                   " not be restored.");
1612
0
    }
1613
0
1614
0
    *out_isGuilty = isGuilty;
1615
0
    return true;
1616
0
}
1617
1618
bool
1619
WebGLContext::TryToRestoreContext()
1620
0
{
1621
0
    if (NS_FAILED(SetDimensions(mRequestedSize.width, mRequestedSize.height)))
1622
0
        return false;
1623
0
1624
0
    return true;
1625
0
}
1626
1627
void
1628
WebGLContext::RunContextLossTimer()
1629
0
{
1630
0
    mContextLossHandler.RunTimer();
1631
0
}
1632
1633
class UpdateContextLossStatusTask : public CancelableRunnable
1634
{
1635
    RefPtr<WebGLContext> mWebGL;
1636
1637
public:
1638
  explicit UpdateContextLossStatusTask(WebGLContext* webgl)
1639
    : CancelableRunnable("UpdateContextLossStatusTask")
1640
    , mWebGL(webgl)
1641
0
  {
1642
0
    }
1643
1644
0
    NS_IMETHOD Run() override {
1645
0
        if (mWebGL)
1646
0
            mWebGL->UpdateContextLossStatus();
1647
0
1648
0
        return NS_OK;
1649
0
    }
1650
1651
0
    nsresult Cancel() override {
1652
0
        mWebGL = nullptr;
1653
0
        return NS_OK;
1654
0
    }
1655
};
1656
1657
void
1658
WebGLContext::EnqueueUpdateContextLossStatus()
1659
0
{
1660
0
    nsCOMPtr<nsIRunnable> task = new UpdateContextLossStatusTask(this);
1661
0
    NS_DispatchToCurrentThread(task);
1662
0
}
1663
1664
// We use this timer for many things. Here are the things that it is activated for:
1665
// 1) If a script is using the MOZ_WEBGL_lose_context extension.
1666
// 2) If we are using EGL and _NOT ANGLE_, we query periodically to see if the
1667
//    CONTEXT_LOST_WEBGL error has been triggered.
1668
// 3) If we are using ANGLE, or anything that supports ARB_robustness, query the
1669
//    GPU periodically to see if the reset status bit has been set.
1670
// In all of these situations, we use this timer to send the script context lost
1671
// and restored events asynchronously. For example, if it triggers a context loss,
1672
// the webglcontextlost event will be sent to it the next time the robustness timer
1673
// fires.
1674
// Note that this timer mechanism is not used unless one of these 3 criteria
1675
// are met.
1676
// At a bare minimum, from context lost to context restores, it would take 3
1677
// full timer iterations: detection, webglcontextlost, webglcontextrestored.
1678
void
1679
WebGLContext::UpdateContextLossStatus()
1680
0
{
1681
0
    if (!mCanvasElement && !mOffscreenCanvas) {
1682
0
        // the canvas is gone. That happens when the page was closed before we got
1683
0
        // this timer event. In this case, there's nothing to do here, just don't crash.
1684
0
        return;
1685
0
    }
1686
0
    if (mContextStatus == ContextStatus::NotLost) {
1687
0
        // We don't know that we're lost, but we might be, so we need to
1688
0
        // check. If we're guilty, don't allow restores, though.
1689
0
1690
0
        bool isGuilty = true;
1691
0
        MOZ_ASSERT(gl); // Shouldn't be missing gl if we're NotLost.
1692
0
        bool isContextLost = CheckContextLost(gl, &isGuilty);
1693
0
1694
0
        if (isContextLost) {
1695
0
            if (isGuilty)
1696
0
                mAllowContextRestore = false;
1697
0
1698
0
            ForceLoseContext();
1699
0
        }
1700
0
1701
0
        // Fall through.
1702
0
    }
1703
0
1704
0
    if (mContextStatus == ContextStatus::LostAwaitingEvent) {
1705
0
        // The context has been lost and we haven't yet triggered the
1706
0
        // callback, so do that now.
1707
0
        const auto kEventName = NS_LITERAL_STRING("webglcontextlost");
1708
0
        const auto kCanBubble = CanBubble::eYes;
1709
0
        const auto kIsCancelable = Cancelable::eYes;
1710
0
        bool useDefaultHandler;
1711
0
1712
0
        if (mCanvasElement) {
1713
0
            nsContentUtils::DispatchTrustedEvent(
1714
0
                mCanvasElement->OwnerDoc(),
1715
0
                static_cast<nsIContent*>(mCanvasElement),
1716
0
                kEventName,
1717
0
                kCanBubble,
1718
0
                kIsCancelable,
1719
0
                &useDefaultHandler);
1720
0
        } else {
1721
0
            // OffscreenCanvas case
1722
0
            RefPtr<Event> event = new Event(mOffscreenCanvas, nullptr, nullptr);
1723
0
            event->InitEvent(kEventName, kCanBubble, kIsCancelable);
1724
0
            event->SetTrusted(true);
1725
0
            useDefaultHandler =
1726
0
                mOffscreenCanvas->DispatchEvent(*event, CallerType::System,
1727
0
                                                IgnoreErrors());
1728
0
        }
1729
0
1730
0
        // We sent the callback, so we're just 'regular lost' now.
1731
0
        mContextStatus = ContextStatus::Lost;
1732
0
        // If we're told to use the default handler, it means the script
1733
0
        // didn't bother to handle the event. In this case, we shouldn't
1734
0
        // auto-restore the context.
1735
0
        if (useDefaultHandler)
1736
0
            mAllowContextRestore = false;
1737
0
1738
0
        // Fall through.
1739
0
    }
1740
0
1741
0
    if (mContextStatus == ContextStatus::Lost) {
1742
0
        // Context is lost, and we've already sent the callback. We
1743
0
        // should try to restore the context if we're both allowed to,
1744
0
        // and supposed to.
1745
0
1746
0
        // Are we allowed to restore the context?
1747
0
        if (!mAllowContextRestore)
1748
0
            return;
1749
0
1750
0
        // If we're only simulated-lost, we shouldn't auto-restore, and
1751
0
        // instead we should wait for restoreContext() to be called.
1752
0
        if (mLastLossWasSimulated)
1753
0
            return;
1754
0
1755
0
        // Restore when the app is visible
1756
0
        if (mRestoreWhenVisible)
1757
0
            return;
1758
0
1759
0
        ForceRestoreContext();
1760
0
        return;
1761
0
    }
1762
0
1763
0
    if (mContextStatus == ContextStatus::LostAwaitingRestore) {
1764
0
        // Context is lost, but we should try to restore it.
1765
0
1766
0
        if (!mAllowContextRestore) {
1767
0
            // We might decide this after thinking we'd be OK restoring
1768
0
            // the context, so downgrade.
1769
0
            mContextStatus = ContextStatus::Lost;
1770
0
            return;
1771
0
        }
1772
0
1773
0
        if (!TryToRestoreContext()) {
1774
0
            // Failed to restore. Try again later.
1775
0
            mContextLossHandler.RunTimer();
1776
0
            return;
1777
0
        }
1778
0
1779
0
        // Revival!
1780
0
        mContextStatus = ContextStatus::NotLost;
1781
0
1782
0
        if (mCanvasElement) {
1783
0
            nsContentUtils::DispatchTrustedEvent(
1784
0
                mCanvasElement->OwnerDoc(),
1785
0
                static_cast<nsIContent*>(mCanvasElement),
1786
0
                NS_LITERAL_STRING("webglcontextrestored"),
1787
0
                CanBubble::eYes,
1788
0
                Cancelable::eYes);
1789
0
        } else {
1790
0
            RefPtr<Event> event = new Event(mOffscreenCanvas, nullptr, nullptr);
1791
0
            event->InitEvent(NS_LITERAL_STRING("webglcontextrestored"),
1792
0
                             CanBubble::eYes,
1793
0
                             Cancelable::eYes);
1794
0
            event->SetTrusted(true);
1795
0
            mOffscreenCanvas->DispatchEvent(*event);
1796
0
        }
1797
0
1798
0
        mEmitContextLostErrorOnce = true;
1799
0
        return;
1800
0
    }
1801
0
}
1802
1803
void
1804
WebGLContext::ForceLoseContext(bool simulateLosing)
1805
0
{
1806
0
    printf_stderr("WebGL(%p)::ForceLoseContext\n", this);
1807
0
    MOZ_ASSERT(gl);
1808
0
    mContextStatus = ContextStatus::LostAwaitingEvent;
1809
0
    mContextLostErrorSet = false;
1810
0
1811
0
    // Burn it all!
1812
0
    DestroyResourcesAndContext();
1813
0
    mLastLossWasSimulated = simulateLosing;
1814
0
1815
0
    // Queue up a task, since we know the status changed.
1816
0
    EnqueueUpdateContextLossStatus();
1817
0
}
1818
1819
void
1820
WebGLContext::ForceRestoreContext()
1821
0
{
1822
0
    printf_stderr("WebGL(%p)::ForceRestoreContext\n", this);
1823
0
    mContextStatus = ContextStatus::LostAwaitingRestore;
1824
0
    mAllowContextRestore = true; // Hey, you did say 'force'.
1825
0
1826
0
    // Queue up a task, since we know the status changed.
1827
0
    EnqueueUpdateContextLossStatus();
1828
0
}
1829
1830
already_AddRefed<mozilla::gfx::SourceSurface>
1831
WebGLContext::GetSurfaceSnapshot(gfxAlphaType* const out_alphaType)
1832
0
{
1833
0
    const FuncScope funcScope(*this, "<GetSurfaceSnapshot>");
1834
0
    if (IsContextLost())
1835
0
        return nullptr;
1836
0
1837
0
    if (!BindDefaultFBForRead())
1838
0
        return nullptr;
1839
0
1840
0
    const auto surfFormat = mOptions.alpha ? SurfaceFormat::B8G8R8A8
1841
0
                                           : SurfaceFormat::B8G8R8X8;
1842
0
    const auto& size = mDefaultFB->mSize;
1843
0
    RefPtr<DataSourceSurface> surf;
1844
0
    surf = Factory::CreateDataSourceSurfaceWithStride(size, surfFormat, size.width * 4);
1845
0
    if (NS_WARN_IF(!surf))
1846
0
        return nullptr;
1847
0
1848
0
    ReadPixelsIntoDataSurface(gl, surf);
1849
0
1850
0
    gfxAlphaType alphaType;
1851
0
    if (!mOptions.alpha) {
1852
0
        alphaType = gfxAlphaType::Opaque;
1853
0
    } else if (mOptions.premultipliedAlpha) {
1854
0
        alphaType = gfxAlphaType::Premult;
1855
0
    } else {
1856
0
        alphaType = gfxAlphaType::NonPremult;
1857
0
    }
1858
0
1859
0
    if (out_alphaType) {
1860
0
        *out_alphaType = alphaType;
1861
0
    } else {
1862
0
        // Expects Opaque or Premult
1863
0
        if (alphaType == gfxAlphaType::NonPremult) {
1864
0
            gfxUtils::PremultiplyDataSurface(surf, surf);
1865
0
        }
1866
0
    }
1867
0
1868
0
    RefPtr<DrawTarget> dt =
1869
0
        Factory::CreateDrawTarget(gfxPlatform::GetPlatform()->GetSoftwareBackend(),
1870
0
                                  size, SurfaceFormat::B8G8R8A8);
1871
0
    if (!dt)
1872
0
        return nullptr;
1873
0
1874
0
    dt->SetTransform(Matrix::Translation(0.0, size.height).PreScale(1.0, -1.0));
1875
0
1876
0
    const gfx::Rect rect{0, 0, float(size.width), float(size.height)};
1877
0
    dt->DrawSurface(surf, rect, rect, DrawSurfaceOptions(),
1878
0
                    DrawOptions(1.0f, CompositionOp::OP_SOURCE));
1879
0
1880
0
    return dt->Snapshot();
1881
0
}
1882
1883
void
1884
WebGLContext::DidRefresh()
1885
0
{
1886
0
    if (gl) {
1887
0
        gl->FlushIfHeavyGLCallsSinceLastFlush();
1888
0
    }
1889
0
}
1890
1891
////////////////////////////////////////////////////////////////////////////////
1892
1893
gfx::IntSize
1894
WebGLContext::DrawingBufferSize()
1895
0
{
1896
0
    const gfx::IntSize zeros{0, 0};
1897
0
    if (IsContextLost())
1898
0
        return zeros;
1899
0
1900
0
    if (!EnsureDefaultFB())
1901
0
        return zeros;
1902
0
1903
0
    return mDefaultFB->mSize;
1904
0
}
1905
1906
bool
1907
WebGLContext::ValidateAndInitFB(const WebGLFramebuffer* const fb)
1908
0
{
1909
0
    if (fb)
1910
0
        return fb->ValidateAndInitAttachments();
1911
0
1912
0
    if (!EnsureDefaultFB())
1913
0
        return false;
1914
0
1915
0
    if (mDefaultFB_IsInvalid) {
1916
0
        gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mDefaultFB->mFB);
1917
0
        const GLbitfield bits = LOCAL_GL_COLOR_BUFFER_BIT |
1918
0
                                LOCAL_GL_DEPTH_BUFFER_BIT |
1919
0
                                LOCAL_GL_STENCIL_BUFFER_BIT;
1920
0
        const bool fakeNoAlpha = !mOptions.alpha;
1921
0
        ForceClearFramebufferWithDefaultValues(bits, fakeNoAlpha);
1922
0
        mDefaultFB_IsInvalid = false;
1923
0
    }
1924
0
    return true;
1925
0
}
1926
1927
void
1928
WebGLContext::DoBindFB(const WebGLFramebuffer* const fb, const GLenum target) const
1929
0
{
1930
0
    const GLenum driverFB = fb ? fb->mGLName : mDefaultFB->mFB;
1931
0
    gl->fBindFramebuffer(target, driverFB);
1932
0
}
1933
1934
bool
1935
WebGLContext::BindCurFBForDraw()
1936
0
{
1937
0
    const auto& fb = mBoundDrawFramebuffer;
1938
0
    if (!ValidateAndInitFB(fb))
1939
0
        return false;
1940
0
1941
0
    DoBindFB(fb);
1942
0
    return true;
1943
0
}
1944
1945
bool
1946
WebGLContext::BindCurFBForColorRead(const webgl::FormatUsageInfo** const out_format,
1947
                                    uint32_t* const out_width,
1948
                                    uint32_t* const out_height)
1949
0
{
1950
0
    const auto& fb = mBoundReadFramebuffer;
1951
0
1952
0
    if (fb) {
1953
0
        if (!ValidateAndInitFB(fb))
1954
0
            return false;
1955
0
        if (!fb->ValidateForColorRead(out_format, out_width, out_height))
1956
0
            return false;
1957
0
1958
0
        gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fb->mGLName);
1959
0
        return true;
1960
0
    }
1961
0
1962
0
    if (!BindDefaultFBForRead())
1963
0
        return false;
1964
0
1965
0
    if (mDefaultFB_ReadBuffer == LOCAL_GL_NONE) {
1966
0
        ErrorInvalidOperation("Can't read from backbuffer when readBuffer mode is NONE.");
1967
0
        return false;
1968
0
    }
1969
0
1970
0
    auto effFormat = mOptions.alpha ? webgl::EffectiveFormat::RGBA8
1971
0
                                    : webgl::EffectiveFormat::RGB8;
1972
0
1973
0
    *out_format = mFormatUsage->GetUsage(effFormat);
1974
0
    MOZ_ASSERT(*out_format);
1975
0
1976
0
    *out_width = mDefaultFB->mSize.width;
1977
0
    *out_height = mDefaultFB->mSize.height;
1978
0
    return true;
1979
0
}
1980
1981
bool
1982
WebGLContext::BindDefaultFBForRead()
1983
0
{
1984
0
    if (!ValidateAndInitFB(nullptr))
1985
0
        return false;
1986
0
1987
0
    if (!mDefaultFB->mSamples) {
1988
0
        gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mDefaultFB->mFB);
1989
0
        return true;
1990
0
    }
1991
0
1992
0
    if (!mResolvedDefaultFB) {
1993
0
        mResolvedDefaultFB = MozFramebuffer::Create(gl, mDefaultFB->mSize, 0, false);
1994
0
        if (!mResolvedDefaultFB) {
1995
0
            gfxCriticalNote << FuncName() << ": Failed to create mResolvedDefaultFB.";
1996
0
            return false;
1997
0
        }
1998
0
    }
1999
0
2000
0
    gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mResolvedDefaultFB->mFB);
2001
0
    BlitBackbufferToCurDriverFB();
2002
0
2003
0
    gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mResolvedDefaultFB->mFB);
2004
0
    return true;
2005
0
}
2006
2007
void
2008
WebGLContext::DoColorMask(const uint8_t bitmask) const
2009
0
{
2010
0
    if (mDriverColorMask != bitmask) {
2011
0
        mDriverColorMask = bitmask;
2012
0
        gl->fColorMask(bool(mDriverColorMask & (1 << 0)),
2013
0
                       bool(mDriverColorMask & (1 << 1)),
2014
0
                       bool(mDriverColorMask & (1 << 2)),
2015
0
                       bool(mDriverColorMask & (1 << 3)));
2016
0
    }
2017
0
}
2018
2019
////////////////////////////////////////////////////////////////////////////////
2020
2021
ScopedDrawCallWrapper::ScopedDrawCallWrapper(WebGLContext& webgl)
2022
    : mWebGL(webgl)
2023
0
{
2024
0
    uint8_t driverColorMask = mWebGL.mColorWriteMask;
2025
0
    bool driverDepthTest    = mWebGL.mDepthTestEnabled;
2026
0
    bool driverStencilTest  = mWebGL.mStencilTestEnabled;
2027
0
    const auto& fb = mWebGL.mBoundDrawFramebuffer;
2028
0
    if (!fb) {
2029
0
        if (mWebGL.mDefaultFB_DrawBuffer0 == LOCAL_GL_NONE) {
2030
0
            driverColorMask = 0; // Is this well-optimized enough for depth-first
2031
0
                                 // rendering?
2032
0
        } else {
2033
0
            driverColorMask &= ~(uint8_t(mWebGL.mNeedsFakeNoAlpha) << 3);
2034
0
        }
2035
0
        driverDepthTest   &= !mWebGL.mNeedsFakeNoDepth;
2036
0
        driverStencilTest &= !mWebGL.mNeedsFakeNoStencil;
2037
0
    } else {
2038
0
        if (mWebGL.mNeedsFakeNoStencil_UserFBs &&
2039
0
            fb->DepthAttachment().IsDefined() &&
2040
0
            !fb->StencilAttachment().IsDefined())
2041
0
        {
2042
0
            driverStencilTest = false;
2043
0
        }
2044
0
    }
2045
0
2046
0
    const auto& gl = mWebGL.gl;
2047
0
    mWebGL.DoColorMask(driverColorMask);
2048
0
    if (mWebGL.mDriverDepthTest != driverDepthTest) {
2049
0
        // "When disabled, the depth comparison and subsequent possible updates to the
2050
0
        //  depth buffer value are bypassed and the fragment is passed to the next
2051
0
        //  operation." [GLES 3.0.5, p177]
2052
0
        mWebGL.mDriverDepthTest = driverDepthTest;
2053
0
        gl->SetEnabled(LOCAL_GL_DEPTH_TEST, mWebGL.mDriverDepthTest);
2054
0
    }
2055
0
    if (mWebGL.mDriverStencilTest != driverStencilTest) {
2056
0
        // "When disabled, the stencil test and associated modifications are not made, and
2057
0
        //  the fragment is always passed." [GLES 3.0.5, p175]
2058
0
        mWebGL.mDriverStencilTest = driverStencilTest;
2059
0
        gl->SetEnabled(LOCAL_GL_STENCIL_TEST, mWebGL.mDriverStencilTest);
2060
0
    }
2061
0
}
2062
2063
ScopedDrawCallWrapper::~ScopedDrawCallWrapper()
2064
0
{
2065
0
    if (mWebGL.mBoundDrawFramebuffer)
2066
0
        return;
2067
0
2068
0
    mWebGL.mResolvedDefaultFB = nullptr;
2069
0
2070
0
    mWebGL.Invalidate();
2071
0
    mWebGL.mShouldPresent = true;
2072
0
}
2073
2074
////////////////////////////////////////
2075
2076
IndexedBufferBinding::IndexedBufferBinding()
2077
    : mRangeStart(0)
2078
    , mRangeSize(0)
2079
0
{ }
2080
2081
uint64_t
2082
IndexedBufferBinding::ByteCount() const
2083
0
{
2084
0
    if (!mBufferBinding)
2085
0
        return 0;
2086
0
2087
0
    uint64_t bufferSize = mBufferBinding->ByteLength();
2088
0
    if (!mRangeSize) // BindBufferBase
2089
0
        return bufferSize;
2090
0
2091
0
    if (mRangeStart >= bufferSize)
2092
0
        return 0;
2093
0
    bufferSize -= mRangeStart;
2094
0
2095
0
    return std::min(bufferSize, mRangeSize);
2096
0
}
2097
2098
////////////////////////////////////////
2099
2100
ScopedUnpackReset::ScopedUnpackReset(WebGLContext* webgl)
2101
    : ScopedGLWrapper<ScopedUnpackReset>(webgl->gl)
2102
    , mWebGL(webgl)
2103
0
{
2104
0
    if (mWebGL->mPixelStore_UnpackAlignment != 4) mGL->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
2105
0
2106
0
    if (mWebGL->IsWebGL2()) {
2107
0
        if (mWebGL->mPixelStore_UnpackRowLength   != 0) mGL->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH  , 0);
2108
0
        if (mWebGL->mPixelStore_UnpackImageHeight != 0) mGL->fPixelStorei(LOCAL_GL_UNPACK_IMAGE_HEIGHT, 0);
2109
0
        if (mWebGL->mPixelStore_UnpackSkipPixels  != 0) mGL->fPixelStorei(LOCAL_GL_UNPACK_SKIP_PIXELS , 0);
2110
0
        if (mWebGL->mPixelStore_UnpackSkipRows    != 0) mGL->fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS   , 0);
2111
0
        if (mWebGL->mPixelStore_UnpackSkipImages  != 0) mGL->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES , 0);
2112
0
2113
0
        if (mWebGL->mBoundPixelUnpackBuffer) mGL->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0);
2114
0
    }
2115
0
}
2116
2117
void
2118
ScopedUnpackReset::UnwrapImpl()
2119
0
{
2120
0
    mGL->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, mWebGL->mPixelStore_UnpackAlignment);
2121
0
2122
0
    if (mWebGL->IsWebGL2()) {
2123
0
        mGL->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH  , mWebGL->mPixelStore_UnpackRowLength  );
2124
0
        mGL->fPixelStorei(LOCAL_GL_UNPACK_IMAGE_HEIGHT, mWebGL->mPixelStore_UnpackImageHeight);
2125
0
        mGL->fPixelStorei(LOCAL_GL_UNPACK_SKIP_PIXELS , mWebGL->mPixelStore_UnpackSkipPixels );
2126
0
        mGL->fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS   , mWebGL->mPixelStore_UnpackSkipRows   );
2127
0
        mGL->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES , mWebGL->mPixelStore_UnpackSkipImages );
2128
0
2129
0
        GLuint pbo = 0;
2130
0
        if (mWebGL->mBoundPixelUnpackBuffer) {
2131
0
            pbo = mWebGL->mBoundPixelUnpackBuffer->mGLName;
2132
0
        }
2133
0
2134
0
        mGL->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, pbo);
2135
0
    }
2136
0
}
2137
2138
////////////////////
2139
2140
void
2141
ScopedFBRebinder::UnwrapImpl()
2142
0
{
2143
0
    const auto fnName = [&](WebGLFramebuffer* fb) {
2144
0
        return fb ? fb->mGLName : 0;
2145
0
    };
2146
0
2147
0
    if (mWebGL->IsWebGL2()) {
2148
0
        mGL->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, fnName(mWebGL->mBoundDrawFramebuffer));
2149
0
        mGL->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, fnName(mWebGL->mBoundReadFramebuffer));
2150
0
    } else {
2151
0
        MOZ_ASSERT(mWebGL->mBoundDrawFramebuffer == mWebGL->mBoundReadFramebuffer);
2152
0
        mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fnName(mWebGL->mBoundDrawFramebuffer));
2153
0
    }
2154
0
}
2155
2156
////////////////////
2157
2158
static GLenum
2159
TargetIfLazy(GLenum target)
2160
0
{
2161
0
    switch (target) {
2162
0
    case LOCAL_GL_PIXEL_PACK_BUFFER:
2163
0
    case LOCAL_GL_PIXEL_UNPACK_BUFFER:
2164
0
        return target;
2165
0
2166
0
    default:
2167
0
        return 0;
2168
0
    }
2169
0
}
2170
2171
ScopedLazyBind::ScopedLazyBind(gl::GLContext* gl, GLenum target, const WebGLBuffer* buf)
2172
    : ScopedGLWrapper<ScopedLazyBind>(gl)
2173
    , mTarget(buf ? TargetIfLazy(target) : 0)
2174
    , mBuf(buf)
2175
0
{
2176
0
    if (mTarget) {
2177
0
        mGL->fBindBuffer(mTarget, mBuf->mGLName);
2178
0
    }
2179
0
}
2180
2181
void
2182
ScopedLazyBind::UnwrapImpl()
2183
0
{
2184
0
    if (mTarget) {
2185
0
        mGL->fBindBuffer(mTarget, 0);
2186
0
    }
2187
0
}
2188
2189
////////////////////////////////////////
2190
2191
bool
2192
Intersect(const int32_t srcSize, const int32_t read0, const int32_t readSize,
2193
          int32_t* const out_intRead0, int32_t* const out_intWrite0,
2194
          int32_t* const out_intSize)
2195
0
{
2196
0
    MOZ_ASSERT(srcSize >= 0);
2197
0
    MOZ_ASSERT(readSize >= 0);
2198
0
    const auto read1 = int64_t(read0) + readSize;
2199
0
2200
0
    int32_t intRead0 = read0; // Clearly doesn't need validation.
2201
0
    int64_t intWrite0 = 0;
2202
0
    int64_t intSize = readSize;
2203
0
2204
0
    if (read1 <= 0 || read0 >= srcSize) {
2205
0
        // Disjoint ranges.
2206
0
        intSize = 0;
2207
0
    } else {
2208
0
        if (read0 < 0) {
2209
0
            const auto diff = int64_t(0) - read0;
2210
0
            MOZ_ASSERT(diff >= 0);
2211
0
            intRead0 = 0;
2212
0
            intWrite0 = diff;
2213
0
            intSize -= diff;
2214
0
        }
2215
0
        if (read1 > srcSize) {
2216
0
            const auto diff = int64_t(read1) - srcSize;
2217
0
            MOZ_ASSERT(diff >= 0);
2218
0
            intSize -= diff;
2219
0
        }
2220
0
2221
0
        if (!CheckedInt<int32_t>(intWrite0).isValid() ||
2222
0
            !CheckedInt<int32_t>(intSize).isValid())
2223
0
        {
2224
0
            return false;
2225
0
        }
2226
0
    }
2227
0
2228
0
    *out_intRead0 = intRead0;
2229
0
    *out_intWrite0 = intWrite0;
2230
0
    *out_intSize = intSize;
2231
0
    return true;
2232
0
}
2233
2234
// --
2235
2236
uint64_t
2237
AvailGroups(const uint64_t totalAvailItems, const uint64_t firstItemOffset,
2238
            const uint32_t groupSize, const uint32_t groupStride)
2239
0
{
2240
0
    MOZ_ASSERT(groupSize && groupStride);
2241
0
    MOZ_ASSERT(groupSize <= groupStride);
2242
0
2243
0
    if (totalAvailItems <= firstItemOffset)
2244
0
        return 0;
2245
0
    const size_t availItems = totalAvailItems - firstItemOffset;
2246
0
2247
0
    size_t availGroups     = availItems / groupStride;
2248
0
    const size_t tailItems = availItems % groupStride;
2249
0
    if (tailItems >= groupSize) {
2250
0
        availGroups += 1;
2251
0
    }
2252
0
    return availGroups;
2253
0
}
2254
2255
////////////////////////////////////////////////////////////////////////////////
2256
2257
CheckedUint32
2258
WebGLContext::GetUnpackSize(bool isFunc3D, uint32_t width, uint32_t height,
2259
                            uint32_t depth, uint8_t bytesPerPixel)
2260
0
{
2261
0
    if (!width || !height || !depth)
2262
0
        return 0;
2263
0
2264
0
    ////////////////
2265
0
2266
0
    const auto& maybeRowLength = mPixelStore_UnpackRowLength;
2267
0
    const auto& maybeImageHeight = mPixelStore_UnpackImageHeight;
2268
0
2269
0
    const auto usedPixelsPerRow = CheckedUint32(mPixelStore_UnpackSkipPixels) + width;
2270
0
    const auto stridePixelsPerRow = (maybeRowLength ? CheckedUint32(maybeRowLength)
2271
0
                                                    : usedPixelsPerRow);
2272
0
2273
0
    const auto usedRowsPerImage = CheckedUint32(mPixelStore_UnpackSkipRows) + height;
2274
0
    const auto strideRowsPerImage = (maybeImageHeight ? CheckedUint32(maybeImageHeight)
2275
0
                                                      : usedRowsPerImage);
2276
0
2277
0
    const uint32_t skipImages = (isFunc3D ? mPixelStore_UnpackSkipImages
2278
0
                                          : 0);
2279
0
    const CheckedUint32 usedImages = CheckedUint32(skipImages) + depth;
2280
0
2281
0
    ////////////////
2282
0
2283
0
    CheckedUint32 strideBytesPerRow = bytesPerPixel * stridePixelsPerRow;
2284
0
    strideBytesPerRow = RoundUpToMultipleOf(strideBytesPerRow,
2285
0
                                            mPixelStore_UnpackAlignment);
2286
0
2287
0
    const CheckedUint32 strideBytesPerImage = strideBytesPerRow * strideRowsPerImage;
2288
0
2289
0
    ////////////////
2290
0
2291
0
    CheckedUint32 usedBytesPerRow = bytesPerPixel * usedPixelsPerRow;
2292
0
    // Don't round this to the alignment, since alignment here is really just used for
2293
0
    // establishing stride, particularly in WebGL 1, where you can't set ROW_LENGTH.
2294
0
2295
0
    CheckedUint32 totalBytes = strideBytesPerImage * (usedImages - 1);
2296
0
    totalBytes += strideBytesPerRow * (usedRowsPerImage - 1);
2297
0
    totalBytes += usedBytesPerRow;
2298
0
2299
0
    return totalBytes;
2300
0
}
2301
2302
2303
#if defined(MOZ_WIDGET_ANDROID)
2304
already_AddRefed<layers::SharedSurfaceTextureClient>
2305
WebGLContext::GetVRFrame()
2306
{
2307
    if (!gl)
2308
        return nullptr;
2309
2310
    // Create a custom GLScreenBuffer for VR.
2311
    if (!mVRScreen) {
2312
        auto caps = gl->Screen()->mCaps;
2313
        mVRScreen = GLScreenBuffer::Create(gl, gfx::IntSize(1, 1), caps);
2314
2315
        RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton();
2316
        if (imageBridge) {
2317
            TextureFlags flags = TextureFlags::ORIGIN_BOTTOM_LEFT;
2318
            UniquePtr<gl::SurfaceFactory> factory = gl::GLScreenBuffer::CreateFactory(gl, caps, imageBridge.get(), flags);
2319
            mVRScreen->Morph(std::move(factory));
2320
        }
2321
    }
2322
2323
    // Swap buffers as though composition has occurred.
2324
    // We will then share the resulting front buffer to be submitted to the VR compositor.
2325
    BeginComposition(mVRScreen.get());
2326
    EndComposition();
2327
2328
    if (IsContextLost())
2329
        return nullptr;
2330
2331
    RefPtr<SharedSurfaceTextureClient> sharedSurface = mVRScreen->Front();
2332
    if (!sharedSurface || !sharedSurface->Surf())
2333
        return nullptr;
2334
2335
    // Make sure that the WebGL buffer is committed to the attached SurfaceTexture on Android.
2336
    sharedSurface->Surf()->ProducerAcquire();
2337
    sharedSurface->Surf()->Commit();
2338
    sharedSurface->Surf()->ProducerRelease();
2339
2340
    return sharedSurface.forget();
2341
}
2342
#else
2343
already_AddRefed<layers::SharedSurfaceTextureClient>
2344
WebGLContext::GetVRFrame()
2345
0
{
2346
0
  /**
2347
0
   * Swap buffers as though composition has occurred.
2348
0
   * We will then share the resulting front buffer to be submitted to the VR
2349
0
   * compositor.
2350
0
   */
2351
0
  BeginComposition();
2352
0
  EndComposition();
2353
0
2354
0
  if (!gl)
2355
0
      return nullptr;
2356
0
2357
0
  gl::GLScreenBuffer* screen = gl->Screen();
2358
0
  if (!screen)
2359
0
      return nullptr;
2360
0
2361
0
  RefPtr<SharedSurfaceTextureClient> sharedSurface = screen->Front();
2362
0
  if (!sharedSurface)
2363
0
      return nullptr;
2364
0
2365
0
  return sharedSurface.forget();
2366
0
}
2367
2368
#endif  // ifdefined(MOZ_WIDGET_ANDROID)
2369
2370
////////////////////////////////////////////////////////////////////////////////
2371
2372
static inline size_t
2373
SizeOfViewElem(const dom::ArrayBufferView& view)
2374
0
{
2375
0
    const auto& elemType = view.Type();
2376
0
    if (elemType == js::Scalar::MaxTypedArrayViewType) // DataViews.
2377
0
        return 1;
2378
0
2379
0
    return js::Scalar::byteSize(elemType);
2380
0
}
2381
2382
bool
2383
WebGLContext::ValidateArrayBufferView(const dom::ArrayBufferView& view, GLuint elemOffset,
2384
                                      GLuint elemCountOverride, uint8_t** const out_bytes,
2385
                                      size_t* const out_byteLen)
2386
0
{
2387
0
    view.ComputeLengthAndData();
2388
0
    uint8_t* const bytes = view.DataAllowShared();
2389
0
    const size_t byteLen = view.LengthAllowShared();
2390
0
2391
0
    const auto& elemSize = SizeOfViewElem(view);
2392
0
2393
0
    size_t elemCount = byteLen / elemSize;
2394
0
    if (elemOffset > elemCount) {
2395
0
        ErrorInvalidValue("Invalid offset into ArrayBufferView.");
2396
0
        return false;
2397
0
    }
2398
0
    elemCount -= elemOffset;
2399
0
2400
0
    if (elemCountOverride) {
2401
0
        if (elemCountOverride > elemCount) {
2402
0
            ErrorInvalidValue("Invalid sub-length for ArrayBufferView.");
2403
0
            return false;
2404
0
        }
2405
0
        elemCount = elemCountOverride;
2406
0
    }
2407
0
2408
0
    *out_bytes = bytes + (elemOffset * elemSize);
2409
0
    *out_byteLen = elemCount * elemSize;
2410
0
    return true;
2411
0
}
2412
2413
////
2414
2415
void
2416
WebGLContext::UpdateMaxDrawBuffers()
2417
0
{
2418
0
    mGLMaxColorAttachments = gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_COLOR_ATTACHMENTS);
2419
0
    mGLMaxDrawBuffers = gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_DRAW_BUFFERS);
2420
0
2421
0
    // WEBGL_draw_buffers:
2422
0
    // "The value of the MAX_COLOR_ATTACHMENTS_WEBGL parameter must be greater than or
2423
0
    //  equal to that of the MAX_DRAW_BUFFERS_WEBGL parameter."
2424
0
    mGLMaxDrawBuffers = std::min(mGLMaxDrawBuffers, mGLMaxColorAttachments);
2425
0
}
2426
2427
// --
2428
2429
const char*
2430
WebGLContext::FuncName() const
2431
0
{
2432
0
    const char* ret;
2433
0
    if (MOZ_LIKELY( mFuncScope )) {
2434
0
        ret = mFuncScope->mFuncName;
2435
0
    } else {
2436
0
        MOZ_ASSERT(false);
2437
0
        ret = "<funcName unknown>";
2438
0
    }
2439
0
    return ret;
2440
0
}
2441
2442
// -
2443
2444
WebGLContext::FuncScope::FuncScope(const WebGLContext& webgl, const char* const funcName)
2445
    : mWebGL(webgl)
2446
    , mFuncName(bool(mWebGL.mFuncScope) ? nullptr : funcName)
2447
0
{
2448
0
    if (MOZ_UNLIKELY( !mFuncName )) {
2449
#ifdef DEBUG
2450
        mStillNeedsToCheckContextLost = false;
2451
#endif
2452
        return;
2453
0
    }
2454
0
2455
0
    mWebGL.mFuncScope = this;
2456
0
}
2457
2458
WebGLContext::FuncScope::~FuncScope()
2459
0
{
2460
0
    if (MOZ_UNLIKELY( !mFuncName ))
2461
0
        return;
2462
0
2463
0
    MOZ_ASSERT(!mStillNeedsToCheckContextLost);
2464
0
    mWebGL.mFuncScope = nullptr;
2465
0
}
2466
2467
bool
2468
WebGLContext::IsContextLost() const
2469
0
{
2470
0
    if (MOZ_LIKELY( mFuncScope )) {
2471
0
        mFuncScope->OnCheckContextLost();
2472
0
    }
2473
0
    return mContextStatus != ContextStatus::NotLost;
2474
0
}
2475
2476
// --
2477
2478
bool
2479
WebGLContext::ValidateIsObject(const WebGLDeletableObject* const object) const
2480
0
{
2481
0
    if (IsContextLost())
2482
0
        return false;
2483
0
2484
0
    if (!object)
2485
0
        return false;
2486
0
2487
0
    if (!object->IsCompatibleWithContext(this))
2488
0
        return false;
2489
0
2490
0
    return !object->IsDeleted();
2491
0
}
2492
2493
bool
2494
WebGLContext::ValidateDeleteObject(const WebGLDeletableObject* const object)
2495
0
{
2496
0
    if (IsContextLost())
2497
0
        return false;
2498
0
2499
0
    if (!object)
2500
0
        return false;
2501
0
2502
0
    if (!ValidateObjectAllowDeleted("obj", *object))
2503
0
        return false;
2504
0
2505
0
    if (object->IsDeleteRequested())
2506
0
        return false;
2507
0
2508
0
    return true;
2509
0
}
2510
2511
// --
2512
2513
webgl::AvailabilityRunnable*
2514
WebGLContext::EnsureAvailabilityRunnable()
2515
0
{
2516
0
    if (!mAvailabilityRunnable) {
2517
0
        RefPtr<webgl::AvailabilityRunnable> runnable = new webgl::AvailabilityRunnable(this);
2518
0
2519
0
        nsIDocument* document = GetOwnerDoc();
2520
0
        if (document) {
2521
0
            document->Dispatch(TaskCategory::Other, runnable.forget());
2522
0
        } else {
2523
0
            NS_DispatchToCurrentThread(runnable.forget());
2524
0
        }
2525
0
    }
2526
0
    return mAvailabilityRunnable;
2527
0
}
2528
2529
webgl::AvailabilityRunnable::AvailabilityRunnable(WebGLContext* const webgl)
2530
    : Runnable("webgl::AvailabilityRunnable")
2531
    , mWebGL(webgl)
2532
0
{
2533
0
    mWebGL->mAvailabilityRunnable = this;
2534
0
}
2535
2536
webgl::AvailabilityRunnable::~AvailabilityRunnable()
2537
0
{
2538
0
    MOZ_ASSERT(mQueries.empty());
2539
0
    MOZ_ASSERT(mSyncs.empty());
2540
0
}
2541
2542
nsresult
2543
webgl::AvailabilityRunnable::Run()
2544
0
{
2545
0
    for (const auto& cur : mQueries) {
2546
0
        cur->mCanBeAvailable = true;
2547
0
    }
2548
0
    mQueries.clear();
2549
0
2550
0
    for (const auto& cur : mSyncs) {
2551
0
        cur->mCanBeAvailable = true;
2552
0
    }
2553
0
    mSyncs.clear();
2554
0
2555
0
    mWebGL->mAvailabilityRunnable = nullptr;
2556
0
    return NS_OK;
2557
0
}
2558
2559
////////////////////////////////////////////////////////////////////////////////
2560
// XPCOM goop
2561
2562
void
2563
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback,
2564
                            const std::vector<IndexedBufferBinding>& field,
2565
                            const char* name, uint32_t flags)
2566
0
{
2567
0
    for (const auto& cur : field) {
2568
0
        ImplCycleCollectionTraverse(callback, cur.mBufferBinding, name, flags);
2569
0
    }
2570
0
}
2571
2572
void
2573
ImplCycleCollectionUnlink(std::vector<IndexedBufferBinding>& field)
2574
0
{
2575
0
    field.clear();
2576
0
}
2577
2578
////
2579
2580
NS_IMPL_CYCLE_COLLECTING_ADDREF(WebGLContext)
2581
NS_IMPL_CYCLE_COLLECTING_RELEASE(WebGLContext)
2582
2583
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLContext,
2584
  mCanvasElement,
2585
  mOffscreenCanvas,
2586
  mExtensions,
2587
  mBound2DTextures,
2588
  mBoundCubeMapTextures,
2589
  mBound3DTextures,
2590
  mBound2DArrayTextures,
2591
  mBoundSamplers,
2592
  mBoundArrayBuffer,
2593
  mBoundCopyReadBuffer,
2594
  mBoundCopyWriteBuffer,
2595
  mBoundPixelPackBuffer,
2596
  mBoundPixelUnpackBuffer,
2597
  mBoundTransformFeedback,
2598
  mBoundTransformFeedbackBuffer,
2599
  mBoundUniformBuffer,
2600
  mCurrentProgram,
2601
  mBoundDrawFramebuffer,
2602
  mBoundReadFramebuffer,
2603
  mBoundRenderbuffer,
2604
  mBoundVertexArray,
2605
  mDefaultVertexArray,
2606
  mQuerySlot_SamplesPassed,
2607
  mQuerySlot_TFPrimsWritten,
2608
  mQuerySlot_TimeElapsed)
2609
2610
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebGLContext)
2611
0
    NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
2612
0
    NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal)
2613
0
    NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
2614
0
    // If the exact way we cast to nsISupports here ever changes, fix our
2615
0
    // ToSupports() method.
2616
0
    NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsICanvasRenderingContextInternal)
2617
0
NS_INTERFACE_MAP_END
2618
2619
} // namespace mozilla