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 "angle/ShaderLang.h"
10 #include "CanvasUtils.h"
11 #include "gfxPrefs.h"
12 #include "GLContext.h"
13 #include "jsfriendapi.h"
14 #include "mozilla/CheckedInt.h"
15 #include "mozilla/Preferences.h"
16 #include "mozilla/Services.h"
17 #include "nsIObserverService.h"
18 #include "nsPrintfCString.h"
19 #include "WebGLActiveInfo.h"
20 #include "WebGLBuffer.h"
21 #include "WebGLContextUtils.h"
22 #include "WebGLFramebuffer.h"
23 #include "WebGLProgram.h"
24 #include "WebGLRenderbuffer.h"
25 #include "WebGLSampler.h"
26 #include "WebGLShader.h"
27 #include "WebGLTexture.h"
28 #include "WebGLUniformLocation.h"
29 #include "WebGLValidateStrings.h"
30 #include "WebGLVertexArray.h"
31 #include "WebGLVertexAttribData.h"
32 
33 #if defined(MOZ_WIDGET_COCOA)
34 #include "nsCocoaFeatures.h"
35 #endif
36 
37 namespace mozilla {
38 
39 bool
ValidateBlendEquationEnum(GLenum mode,const char * info)40 WebGLContext::ValidateBlendEquationEnum(GLenum mode, const char* info)
41 {
42     switch (mode) {
43     case LOCAL_GL_FUNC_ADD:
44     case LOCAL_GL_FUNC_SUBTRACT:
45     case LOCAL_GL_FUNC_REVERSE_SUBTRACT:
46         return true;
47 
48     case LOCAL_GL_MIN:
49     case LOCAL_GL_MAX:
50         if (IsWebGL2() ||
51             IsExtensionEnabled(WebGLExtensionID::EXT_blend_minmax))
52         {
53             return true;
54         }
55 
56         break;
57 
58     default:
59         break;
60     }
61 
62     ErrorInvalidEnumInfo(info, mode);
63     return false;
64 }
65 
66 bool
ValidateBlendFuncDstEnum(GLenum factor,const char * info)67 WebGLContext::ValidateBlendFuncDstEnum(GLenum factor, const char* info)
68 {
69     switch (factor) {
70     case LOCAL_GL_ZERO:
71     case LOCAL_GL_ONE:
72     case LOCAL_GL_SRC_COLOR:
73     case LOCAL_GL_ONE_MINUS_SRC_COLOR:
74     case LOCAL_GL_DST_COLOR:
75     case LOCAL_GL_ONE_MINUS_DST_COLOR:
76     case LOCAL_GL_SRC_ALPHA:
77     case LOCAL_GL_ONE_MINUS_SRC_ALPHA:
78     case LOCAL_GL_DST_ALPHA:
79     case LOCAL_GL_ONE_MINUS_DST_ALPHA:
80     case LOCAL_GL_CONSTANT_COLOR:
81     case LOCAL_GL_ONE_MINUS_CONSTANT_COLOR:
82     case LOCAL_GL_CONSTANT_ALPHA:
83     case LOCAL_GL_ONE_MINUS_CONSTANT_ALPHA:
84         return true;
85 
86     default:
87         ErrorInvalidEnumInfo(info, factor);
88         return false;
89     }
90 }
91 
92 bool
ValidateBlendFuncSrcEnum(GLenum factor,const char * info)93 WebGLContext::ValidateBlendFuncSrcEnum(GLenum factor, const char* info)
94 {
95     if (factor == LOCAL_GL_SRC_ALPHA_SATURATE)
96         return true;
97 
98     return ValidateBlendFuncDstEnum(factor, info);
99 }
100 
101 bool
ValidateBlendFuncEnumsCompatibility(GLenum sfactor,GLenum dfactor,const char * info)102 WebGLContext::ValidateBlendFuncEnumsCompatibility(GLenum sfactor,
103                                                   GLenum dfactor,
104                                                   const char* info)
105 {
106     bool sfactorIsConstantColor = sfactor == LOCAL_GL_CONSTANT_COLOR ||
107                                   sfactor == LOCAL_GL_ONE_MINUS_CONSTANT_COLOR;
108     bool sfactorIsConstantAlpha = sfactor == LOCAL_GL_CONSTANT_ALPHA ||
109                                   sfactor == LOCAL_GL_ONE_MINUS_CONSTANT_ALPHA;
110     bool dfactorIsConstantColor = dfactor == LOCAL_GL_CONSTANT_COLOR ||
111                                   dfactor == LOCAL_GL_ONE_MINUS_CONSTANT_COLOR;
112     bool dfactorIsConstantAlpha = dfactor == LOCAL_GL_CONSTANT_ALPHA ||
113                                   dfactor == LOCAL_GL_ONE_MINUS_CONSTANT_ALPHA;
114     if ( (sfactorIsConstantColor && dfactorIsConstantAlpha) ||
115          (dfactorIsConstantColor && sfactorIsConstantAlpha) )
116     {
117         ErrorInvalidOperation("%s are mutually incompatible, see section 6.8 in"
118                               " the WebGL 1.0 spec", info);
119         return false;
120     }
121 
122     return true;
123 }
124 
125 bool
ValidateComparisonEnum(GLenum target,const char * info)126 WebGLContext::ValidateComparisonEnum(GLenum target, const char* info)
127 {
128     switch (target) {
129     case LOCAL_GL_NEVER:
130     case LOCAL_GL_LESS:
131     case LOCAL_GL_LEQUAL:
132     case LOCAL_GL_GREATER:
133     case LOCAL_GL_GEQUAL:
134     case LOCAL_GL_EQUAL:
135     case LOCAL_GL_NOTEQUAL:
136     case LOCAL_GL_ALWAYS:
137         return true;
138 
139     default:
140         ErrorInvalidEnumInfo(info, target);
141         return false;
142     }
143 }
144 
145 bool
ValidateStencilOpEnum(GLenum action,const char * info)146 WebGLContext::ValidateStencilOpEnum(GLenum action, const char* info)
147 {
148     switch (action) {
149     case LOCAL_GL_KEEP:
150     case LOCAL_GL_ZERO:
151     case LOCAL_GL_REPLACE:
152     case LOCAL_GL_INCR:
153     case LOCAL_GL_INCR_WRAP:
154     case LOCAL_GL_DECR:
155     case LOCAL_GL_DECR_WRAP:
156     case LOCAL_GL_INVERT:
157         return true;
158 
159     default:
160         ErrorInvalidEnumInfo(info, action);
161         return false;
162     }
163 }
164 
165 bool
ValidateFaceEnum(GLenum face,const char * info)166 WebGLContext::ValidateFaceEnum(GLenum face, const char* info)
167 {
168     switch (face) {
169     case LOCAL_GL_FRONT:
170     case LOCAL_GL_BACK:
171     case LOCAL_GL_FRONT_AND_BACK:
172         return true;
173 
174     default:
175         ErrorInvalidEnumInfo(info, face);
176         return false;
177     }
178 }
179 
180 bool
ValidateDrawModeEnum(GLenum mode,const char * info)181 WebGLContext::ValidateDrawModeEnum(GLenum mode, const char* info)
182 {
183     switch (mode) {
184     case LOCAL_GL_TRIANGLES:
185     case LOCAL_GL_TRIANGLE_STRIP:
186     case LOCAL_GL_TRIANGLE_FAN:
187     case LOCAL_GL_POINTS:
188     case LOCAL_GL_LINE_STRIP:
189     case LOCAL_GL_LINE_LOOP:
190     case LOCAL_GL_LINES:
191         return true;
192 
193     default:
194         ErrorInvalidEnumInfo(info, mode);
195         return false;
196     }
197 }
198 
199 bool
ValidateUniformLocation(WebGLUniformLocation * loc,const char * funcName)200 WebGLContext::ValidateUniformLocation(WebGLUniformLocation* loc, const char* funcName)
201 {
202     /* GLES 2.0.25, p38:
203      *   If the value of location is -1, the Uniform* commands will silently
204      *   ignore the data passed in, and the current uniform values will not be
205      *   changed.
206      */
207     if (!loc)
208         return false;
209 
210     if (!ValidateObjectAllowDeleted(funcName, *loc))
211         return false;
212 
213     if (!mCurrentProgram) {
214         ErrorInvalidOperation("%s: No program is currently bound.", funcName);
215         return false;
216     }
217 
218     return loc->ValidateForProgram(mCurrentProgram, funcName);
219 }
220 
221 bool
ValidateAttribArraySetter(const char * name,uint32_t setterElemSize,uint32_t arrayLength)222 WebGLContext::ValidateAttribArraySetter(const char* name, uint32_t setterElemSize,
223                                         uint32_t arrayLength)
224 {
225     if (IsContextLost())
226         return false;
227 
228     if (arrayLength < setterElemSize) {
229         ErrorInvalidValue("%s: Array must have >= %d elements.", name,
230                           setterElemSize);
231         return false;
232     }
233 
234     return true;
235 }
236 
237 bool
ValidateUniformSetter(WebGLUniformLocation * loc,uint8_t setterElemSize,GLenum setterType,const char * funcName)238 WebGLContext::ValidateUniformSetter(WebGLUniformLocation* loc,
239                                     uint8_t setterElemSize, GLenum setterType,
240                                     const char* funcName)
241 {
242     if (IsContextLost())
243         return false;
244 
245     if (!ValidateUniformLocation(loc, funcName))
246         return false;
247 
248     if (!loc->ValidateSizeAndType(setterElemSize, setterType, funcName))
249         return false;
250 
251     return true;
252 }
253 
254 bool
ValidateUniformArraySetter(WebGLUniformLocation * loc,uint8_t setterElemSize,GLenum setterType,uint32_t setterArraySize,const char * funcName,uint32_t * const out_numElementsToUpload)255 WebGLContext::ValidateUniformArraySetter(WebGLUniformLocation* loc,
256                                          uint8_t setterElemSize,
257                                          GLenum setterType,
258                                          uint32_t setterArraySize,
259                                          const char* funcName,
260                                          uint32_t* const out_numElementsToUpload)
261 {
262     if (IsContextLost())
263         return false;
264 
265     if (!ValidateUniformLocation(loc, funcName))
266         return false;
267 
268     if (!loc->ValidateSizeAndType(setterElemSize, setterType, funcName))
269         return false;
270 
271     if (!loc->ValidateArrayLength(setterElemSize, setterArraySize, funcName))
272         return false;
273 
274     const auto& elemCount = loc->mInfo->mActiveInfo->mElemCount;
275     MOZ_ASSERT(elemCount > loc->mArrayIndex);
276     const uint32_t uniformElemCount = elemCount - loc->mArrayIndex;
277 
278     *out_numElementsToUpload = std::min(uniformElemCount,
279                                         setterArraySize / setterElemSize);
280     return true;
281 }
282 
283 bool
ValidateUniformMatrixArraySetter(WebGLUniformLocation * loc,uint8_t setterCols,uint8_t setterRows,GLenum setterType,uint32_t setterArraySize,bool setterTranspose,const char * funcName,uint32_t * const out_numElementsToUpload)284 WebGLContext::ValidateUniformMatrixArraySetter(WebGLUniformLocation* loc,
285                                                uint8_t setterCols,
286                                                uint8_t setterRows,
287                                                GLenum setterType,
288                                                uint32_t setterArraySize,
289                                                bool setterTranspose,
290                                                const char* funcName,
291                                                uint32_t* const out_numElementsToUpload)
292 {
293     const uint8_t setterElemSize = setterCols * setterRows;
294 
295     if (IsContextLost())
296         return false;
297 
298     if (!ValidateUniformLocation(loc, funcName))
299         return false;
300 
301     if (!loc->ValidateSizeAndType(setterElemSize, setterType, funcName))
302         return false;
303 
304     if (!loc->ValidateArrayLength(setterElemSize, setterArraySize, funcName))
305         return false;
306 
307     if (!ValidateUniformMatrixTranspose(setterTranspose, funcName))
308         return false;
309 
310     const auto& elemCount = loc->mInfo->mActiveInfo->mElemCount;
311     MOZ_ASSERT(elemCount > loc->mArrayIndex);
312     const uint32_t uniformElemCount = elemCount - loc->mArrayIndex;
313 
314     *out_numElementsToUpload = std::min(uniformElemCount,
315                                         setterArraySize / setterElemSize);
316     return true;
317 }
318 
319 bool
ValidateAttribIndex(GLuint index,const char * info)320 WebGLContext::ValidateAttribIndex(GLuint index, const char* info)
321 {
322     bool valid = (index < MaxVertexAttribs());
323 
324     if (!valid) {
325         if (index == GLuint(-1)) {
326             ErrorInvalidValue("%s: -1 is not a valid `index`. This value"
327                               " probably comes from a getAttribLocation()"
328                               " call, where this return value -1 means"
329                               " that the passed name didn't correspond to"
330                               " an active attribute in the specified"
331                               " program.", info);
332         } else {
333             ErrorInvalidValue("%s: `index` must be less than"
334                               " MAX_VERTEX_ATTRIBS.", info);
335         }
336     }
337 
338     return valid;
339 }
340 
341 bool
ValidateAttribPointer(bool integerMode,GLuint index,GLint size,GLenum type,WebGLboolean normalized,GLsizei stride,WebGLintptr byteOffset,const char * info)342 WebGLContext::ValidateAttribPointer(bool integerMode, GLuint index, GLint size, GLenum type,
343                                     WebGLboolean normalized, GLsizei stride,
344                                     WebGLintptr byteOffset, const char* info)
345 {
346     WebGLBuffer* buffer = mBoundArrayBuffer;
347     if (!buffer) {
348         ErrorInvalidOperation("%s: must have valid GL_ARRAY_BUFFER binding", info);
349         return false;
350     }
351 
352     uint32_t requiredAlignment = 0;
353     if (!ValidateAttribPointerType(integerMode, type, &requiredAlignment, info))
354         return false;
355 
356     // requiredAlignment should always be a power of two
357     MOZ_ASSERT(IsPowerOfTwo(requiredAlignment));
358     GLsizei requiredAlignmentMask = requiredAlignment - 1;
359 
360     if (size < 1 || size > 4) {
361         ErrorInvalidValue("%s: invalid element size", info);
362         return false;
363     }
364 
365     switch (type) {
366     case LOCAL_GL_INT_2_10_10_10_REV:
367     case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV:
368         if (size != 4) {
369             ErrorInvalidOperation("%s: size must be 4 for this type.", info);
370             return false;
371         }
372         break;
373     }
374 
375     // see WebGL spec section 6.6 "Vertex Attribute Data Stride"
376     if (stride < 0 || stride > 255) {
377         ErrorInvalidValue("%s: negative or too large stride", info);
378         return false;
379     }
380 
381     if (byteOffset < 0) {
382         ErrorInvalidValue("%s: negative offset", info);
383         return false;
384     }
385 
386     if (stride & requiredAlignmentMask) {
387         ErrorInvalidOperation("%s: stride doesn't satisfy the alignment "
388                               "requirement of given type", info);
389         return false;
390     }
391 
392     if (byteOffset & requiredAlignmentMask) {
393         ErrorInvalidOperation("%s: byteOffset doesn't satisfy the alignment "
394                               "requirement of given type", info);
395         return false;
396     }
397 
398     return true;
399 }
400 
401 bool
ValidateStencilParamsForDrawCall()402 WebGLContext::ValidateStencilParamsForDrawCall()
403 {
404     const char msg[] = "%s set different front and back stencil %s. Drawing in"
405                        " this configuration is not allowed.";
406 
407     if (mStencilRefFront != mStencilRefBack) {
408         ErrorInvalidOperation(msg, "stencilFuncSeparate", "reference values");
409         return false;
410     }
411 
412     if (mStencilValueMaskFront != mStencilValueMaskBack) {
413         ErrorInvalidOperation(msg, "stencilFuncSeparate", "value masks");
414         return false;
415     }
416 
417     if (mStencilWriteMaskFront != mStencilWriteMaskBack) {
418         ErrorInvalidOperation(msg, "stencilMaskSeparate", "write masks");
419         return false;
420     }
421 
422     return true;
423 }
424 
425 static inline int32_t
FloorPOT(int32_t x)426 FloorPOT(int32_t x)
427 {
428     MOZ_ASSERT(x > 0);
429     int32_t pot = 1;
430     while (pot < 0x40000000) {
431         if (x < pot*2)
432             break;
433         pot *= 2;
434     }
435     return pot;
436 }
437 
438 bool
InitAndValidateGL(FailureReason * const out_failReason)439 WebGLContext::InitAndValidateGL(FailureReason* const out_failReason)
440 {
441     MOZ_RELEASE_ASSERT(gl, "GFX: GL not initialized");
442 
443     // Unconditionally create a new format usage authority. This is
444     // important when restoring contexts and extensions need to add
445     // formats back into the authority.
446     mFormatUsage = CreateFormatUsage(gl);
447     if (!mFormatUsage) {
448         *out_failReason = { "FEATURE_FAILURE_WEBGL_FORMAT",
449                             "Failed to create mFormatUsage." };
450         return false;
451     }
452 
453     GLenum error = gl->fGetError();
454     if (error != LOCAL_GL_NO_ERROR) {
455         const nsPrintfCString reason("GL error 0x%x occurred during OpenGL context"
456                                      " initialization, before WebGL initialization!",
457                                      error);
458         *out_failReason = { "FEATURE_FAILURE_WEBGL_GLERR_1", reason };
459         return false;
460     }
461 
462     mMinCapability = gfxPrefs::WebGLMinCapabilityMode();
463     mDisableExtensions = gfxPrefs::WebGLDisableExtensions();
464     mLoseContextOnMemoryPressure = gfxPrefs::WebGLLoseContextOnMemoryPressure();
465     mCanLoseContextInForeground = gfxPrefs::WebGLCanLoseContextInForeground();
466     mRestoreWhenVisible = gfxPrefs::WebGLRestoreWhenVisible();
467 
468     if (MinCapabilityMode())
469         mDisableFragHighP = true;
470 
471     // These are the default values, see 6.2 State tables in the
472     // OpenGL ES 2.0.25 spec.
473     mColorWriteMask[0] = 1;
474     mColorWriteMask[1] = 1;
475     mColorWriteMask[2] = 1;
476     mColorWriteMask[3] = 1;
477     mDepthWriteMask = 1;
478     mColorClearValue[0] = 0.f;
479     mColorClearValue[1] = 0.f;
480     mColorClearValue[2] = 0.f;
481     mColorClearValue[3] = 0.f;
482     mDepthClearValue = 1.f;
483     mStencilClearValue = 0;
484     mStencilRefFront = 0;
485     mStencilRefBack = 0;
486 
487     mLineWidth = 1.0;
488 
489     /*
490     // Technically, we should be setting mStencil[...] values to
491     // `allOnes`, but either ANGLE breaks or the SGX540s on Try break.
492     GLuint stencilBits = 0;
493     gl->GetUIntegerv(LOCAL_GL_STENCIL_BITS, &stencilBits);
494     GLuint allOnes = ~(UINT32_MAX << stencilBits);
495     mStencilValueMaskFront = allOnes;
496     mStencilValueMaskBack  = allOnes;
497     mStencilWriteMaskFront = allOnes;
498     mStencilWriteMaskBack  = allOnes;
499     */
500 
501     gl->GetUIntegerv(LOCAL_GL_STENCIL_VALUE_MASK,      &mStencilValueMaskFront);
502     gl->GetUIntegerv(LOCAL_GL_STENCIL_BACK_VALUE_MASK, &mStencilValueMaskBack);
503     gl->GetUIntegerv(LOCAL_GL_STENCIL_WRITEMASK,       &mStencilWriteMaskFront);
504     gl->GetUIntegerv(LOCAL_GL_STENCIL_BACK_WRITEMASK,  &mStencilWriteMaskBack);
505 
506     AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_VALUE_MASK,      mStencilValueMaskFront);
507     AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_BACK_VALUE_MASK, mStencilValueMaskBack);
508     AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_WRITEMASK,       mStencilWriteMaskFront);
509     AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_BACK_WRITEMASK,  mStencilWriteMaskBack);
510 
511     mDitherEnabled = true;
512     mRasterizerDiscardEnabled = false;
513     mScissorTestEnabled = false;
514     mGenerateMipmapHint = LOCAL_GL_DONT_CARE;
515 
516     // Bindings, etc.
517     mActiveTexture = 0;
518     mDefaultFB_DrawBuffer0 = LOCAL_GL_BACK;
519 
520     mEmitContextLostErrorOnce = true;
521     mWebGLError = LOCAL_GL_NO_ERROR;
522     mUnderlyingGLError = LOCAL_GL_NO_ERROR;
523 
524     mBound2DTextures.Clear();
525     mBoundCubeMapTextures.Clear();
526     mBound3DTextures.Clear();
527     mBound2DArrayTextures.Clear();
528     mBoundSamplers.Clear();
529 
530     mBoundArrayBuffer = nullptr;
531     mCurrentProgram = nullptr;
532 
533     mBoundDrawFramebuffer = nullptr;
534     mBoundReadFramebuffer = nullptr;
535     mBoundRenderbuffer = nullptr;
536 
537     MakeContextCurrent();
538 
539     if (MinCapabilityMode())
540         mGLMaxVertexAttribs = MINVALUE_GL_MAX_VERTEX_ATTRIBS;
541     else
542         gl->GetUIntegerv(LOCAL_GL_MAX_VERTEX_ATTRIBS, &mGLMaxVertexAttribs);
543 
544     if (mGLMaxVertexAttribs < 8) {
545         const nsPrintfCString reason("GL_MAX_VERTEX_ATTRIBS: %d is < 8!",
546                                      mGLMaxVertexAttribs);
547         *out_failReason = { "FEATURE_FAILURE_WEBGL_V_ATRB", reason };
548         return false;
549     }
550 
551     // Note: GL_MAX_TEXTURE_UNITS is fixed at 4 for most desktop hardware,
552     // even though the hardware supports much more.  The
553     // GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS value is the accurate value.
554     if (MinCapabilityMode())
555         mGLMaxTextureUnits = MINVALUE_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS;
556     else
557         mGLMaxTextureUnits = gl->GetIntAs<GLint>(LOCAL_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS);
558 
559     if (mGLMaxTextureUnits < 8) {
560         const nsPrintfCString reason("GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: %d is < 8!",
561                                      mGLMaxTextureUnits);
562         *out_failReason = { "FEATURE_FAILURE_WEBGL_T_UNIT", reason };
563         return false;
564     }
565 
566     mBound2DTextures.SetLength(mGLMaxTextureUnits);
567     mBoundCubeMapTextures.SetLength(mGLMaxTextureUnits);
568     mBound3DTextures.SetLength(mGLMaxTextureUnits);
569     mBound2DArrayTextures.SetLength(mGLMaxTextureUnits);
570     mBoundSamplers.SetLength(mGLMaxTextureUnits);
571 
572     gl->fGetIntegerv(LOCAL_GL_MAX_VIEWPORT_DIMS, (GLint*)mImplMaxViewportDims);
573 
574     ////////////////
575 
576     if (MinCapabilityMode()) {
577         mImplMaxTextureSize = MINVALUE_GL_MAX_TEXTURE_SIZE;
578         mImplMaxCubeMapTextureSize = MINVALUE_GL_MAX_CUBE_MAP_TEXTURE_SIZE;
579         mImplMaxRenderbufferSize = MINVALUE_GL_MAX_RENDERBUFFER_SIZE;
580 
581         mImplMax3DTextureSize = MINVALUE_GL_MAX_3D_TEXTURE_SIZE;
582         mImplMaxArrayTextureLayers = MINVALUE_GL_MAX_ARRAY_TEXTURE_LAYERS;
583 
584         mGLMaxTextureImageUnits = MINVALUE_GL_MAX_TEXTURE_IMAGE_UNITS;
585         mGLMaxVertexTextureImageUnits = MINVALUE_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS;
586     } else {
587         gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, (GLint*)&mImplMaxTextureSize);
588         gl->fGetIntegerv(LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE, (GLint*)&mImplMaxCubeMapTextureSize);
589         gl->fGetIntegerv(LOCAL_GL_MAX_RENDERBUFFER_SIZE, (GLint*)&mImplMaxRenderbufferSize);
590 
591         if (!gl->GetPotentialInteger(LOCAL_GL_MAX_3D_TEXTURE_SIZE, (GLint*)&mImplMax3DTextureSize))
592             mImplMax3DTextureSize = 0;
593         if (!gl->GetPotentialInteger(LOCAL_GL_MAX_ARRAY_TEXTURE_LAYERS, (GLint*)&mImplMaxArrayTextureLayers))
594             mImplMaxArrayTextureLayers = 0;
595 
596         gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS, &mGLMaxTextureImageUnits);
597         gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &mGLMaxVertexTextureImageUnits);
598     }
599 
600     // If we don't support a target, its max size is 0. We should only floor-to-POT if the
601     // value if it's non-zero. (NB log2(0) is -Inf, so zero isn't an integer power-of-two)
602     const auto fnFloorPOTIfSupported = [](uint32_t& val) {
603         if (val) {
604             val = FloorPOT(val);
605         }
606     };
607 
608     fnFloorPOTIfSupported(mImplMaxTextureSize);
609     fnFloorPOTIfSupported(mImplMaxCubeMapTextureSize);
610     fnFloorPOTIfSupported(mImplMaxRenderbufferSize);
611 
612     fnFloorPOTIfSupported(mImplMax3DTextureSize);
613     fnFloorPOTIfSupported(mImplMaxArrayTextureLayers);
614 
615     ////////////////
616 
617     mGLMaxColorAttachments = 1;
618     mGLMaxDrawBuffers = 1;
619     gl->GetPotentialInteger(LOCAL_GL_MAX_COLOR_ATTACHMENTS,
620                             (GLint*)&mGLMaxColorAttachments);
621     gl->GetPotentialInteger(LOCAL_GL_MAX_DRAW_BUFFERS, (GLint*)&mGLMaxDrawBuffers);
622 
623     if (MinCapabilityMode()) {
624         mGLMaxColorAttachments = std::min(mGLMaxColorAttachments,
625                                           kMinMaxColorAttachments);
626         mGLMaxDrawBuffers = std::min(mGLMaxDrawBuffers, kMinMaxDrawBuffers);
627     }
628 
629     if (IsWebGL2()) {
630         mImplMaxColorAttachments = mGLMaxColorAttachments;
631         mImplMaxDrawBuffers = std::min(mGLMaxDrawBuffers, mImplMaxColorAttachments);
632     } else {
633         mImplMaxColorAttachments = 1;
634         mImplMaxDrawBuffers = 1;
635     }
636 
637     ////////////////
638 
639     if (MinCapabilityMode()) {
640         mGLMaxFragmentUniformVectors = MINVALUE_GL_MAX_FRAGMENT_UNIFORM_VECTORS;
641         mGLMaxVertexUniformVectors = MINVALUE_GL_MAX_VERTEX_UNIFORM_VECTORS;
642         mGLMaxVaryingVectors = MINVALUE_GL_MAX_VARYING_VECTORS;
643     } else {
644         if (gl->IsSupported(gl::GLFeature::ES2_compatibility)) {
645             gl->fGetIntegerv(LOCAL_GL_MAX_FRAGMENT_UNIFORM_VECTORS, &mGLMaxFragmentUniformVectors);
646             gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_UNIFORM_VECTORS, &mGLMaxVertexUniformVectors);
647             gl->fGetIntegerv(LOCAL_GL_MAX_VARYING_VECTORS, &mGLMaxVaryingVectors);
648         } else {
649             gl->fGetIntegerv(LOCAL_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &mGLMaxFragmentUniformVectors);
650             mGLMaxFragmentUniformVectors /= 4;
651             gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_UNIFORM_COMPONENTS, &mGLMaxVertexUniformVectors);
652             mGLMaxVertexUniformVectors /= 4;
653 
654             /* We are now going to try to read GL_MAX_VERTEX_OUTPUT_COMPONENTS
655              * and GL_MAX_FRAGMENT_INPUT_COMPONENTS, however these constants
656              * only entered the OpenGL standard at OpenGL 3.2. So we will try
657              * reading, and check OpenGL error for INVALID_ENUM.
658              *
659              * On the public_webgl list, "problematic GetParameter pnames"
660              * thread, the following formula was given:
661              *   maxVaryingVectors = min(GL_MAX_VERTEX_OUTPUT_COMPONENTS,
662              *                           GL_MAX_FRAGMENT_INPUT_COMPONENTS) / 4
663              */
664             GLint maxVertexOutputComponents = 0;
665             GLint maxFragmentInputComponents = 0;
666 
667             const bool ok = (gl->GetPotentialInteger(LOCAL_GL_MAX_VERTEX_OUTPUT_COMPONENTS,
668                                                      &maxVertexOutputComponents) &&
669                              gl->GetPotentialInteger(LOCAL_GL_MAX_FRAGMENT_INPUT_COMPONENTS,
670                                                      &maxFragmentInputComponents));
671 
672             if (ok) {
673                 mGLMaxVaryingVectors = std::min(maxVertexOutputComponents,
674                                                 maxFragmentInputComponents) / 4;
675             } else {
676                 mGLMaxVaryingVectors = 16;
677                 // 16 = 64/4, and 64 is the min value for
678                 // maxVertexOutputComponents in the OpenGL 3.2 spec.
679             }
680         }
681     }
682 
683     if (gl->IsCompatibilityProfile()) {
684         gl->fEnable(LOCAL_GL_POINT_SPRITE);
685     }
686 
687     if (!gl->IsGLES()) {
688         gl->fEnable(LOCAL_GL_PROGRAM_POINT_SIZE);
689     }
690 
691 #ifdef XP_MACOSX
692     if (gl->WorkAroundDriverBugs() &&
693         gl->Vendor() == gl::GLVendor::ATI &&
694         !nsCocoaFeatures::IsAtLeastVersion(10,9))
695     {
696         // The Mac ATI driver, in all known OSX version up to and including
697         // 10.8, renders points sprites upside-down. (Apple bug 11778921)
698         gl->fPointParameterf(LOCAL_GL_POINT_SPRITE_COORD_ORIGIN,
699                              LOCAL_GL_LOWER_LEFT);
700     }
701 #endif
702 
703     if (gl->IsSupported(gl::GLFeature::seamless_cube_map_opt_in)) {
704         gl->fEnable(LOCAL_GL_TEXTURE_CUBE_MAP_SEAMLESS);
705     }
706 
707     // Check the shader validator pref
708     mBypassShaderValidation = gfxPrefs::WebGLBypassShaderValidator();
709 
710     // initialize shader translator
711     if (!ShInitialize()) {
712         *out_failReason = { "FEATURE_FAILURE_WEBGL_GLSL",
713                             "GLSL translator initialization failed!" };
714         return false;
715     }
716 
717     // Mesa can only be detected with the GL_VERSION string, of the form
718     // "2.1 Mesa 7.11.0"
719     const char* versionStr = (const char*)(gl->fGetString(LOCAL_GL_VERSION));
720     mIsMesa = strstr(versionStr, "Mesa");
721 
722     // Notice that the point of calling fGetError here is not only to check for
723     // errors, but also to reset the error flags so that a subsequent WebGL
724     // getError call will give the correct result.
725     error = gl->fGetError();
726     if (error != LOCAL_GL_NO_ERROR) {
727         const nsPrintfCString reason("GL error 0x%x occurred during WebGL context"
728                                      " initialization!",
729                                      error);
730         *out_failReason = { "FEATURE_FAILURE_WEBGL_GLERR_2", reason };
731         return false;
732     }
733 
734     if (IsWebGL2() &&
735         !InitWebGL2(out_failReason))
736     {
737         // Todo: Bug 898404: Only allow WebGL2 on GL>=3.0 on desktop GL.
738         return false;
739     }
740 
741     mDefaultVertexArray = WebGLVertexArray::Create(this);
742     mDefaultVertexArray->mAttribs.SetLength(mGLMaxVertexAttribs);
743     mBoundVertexArray = mDefaultVertexArray;
744 
745     // OpenGL core profiles remove the default VAO object from version
746     // 4.0.0. We create a default VAO for all core profiles,
747     // regardless of version.
748     //
749     // GL Spec 4.0.0:
750     // (https://www.opengl.org/registry/doc/glspec40.core.20100311.pdf)
751     // in Section E.2.2 "Removed Features", pg 397: "[...] The default
752     // vertex array object (the name zero) is also deprecated. [...]"
753 
754     if (gl->IsCoreProfile()) {
755         mDefaultVertexArray->GenVertexArray();
756         mDefaultVertexArray->BindVertexArray();
757     }
758 
759     mPixelStore_FlipY = false;
760     mPixelStore_PremultiplyAlpha = false;
761     mPixelStore_ColorspaceConversion = BROWSER_DEFAULT_WEBGL;
762 
763     // GLES 3.0.4, p259:
764     mPixelStore_UnpackImageHeight = 0;
765     mPixelStore_UnpackSkipImages = 0;
766     mPixelStore_UnpackRowLength = 0;
767     mPixelStore_UnpackSkipRows = 0;
768     mPixelStore_UnpackSkipPixels = 0;
769     mPixelStore_UnpackAlignment = 4;
770     mPixelStore_PackRowLength = 0;
771     mPixelStore_PackSkipRows = 0;
772     mPixelStore_PackSkipPixels = 0;
773     mPixelStore_PackAlignment = 4;
774 
775     mPrimRestartTypeBytes = 0;
776 
777     mGenericVertexAttribTypes.reset(new GLenum[mGLMaxVertexAttribs]);
778     std::fill_n(mGenericVertexAttribTypes.get(), mGLMaxVertexAttribs, LOCAL_GL_FLOAT);
779 
780     static const float kDefaultGenericVertexAttribData[4] = { 0, 0, 0, 1 };
781     memcpy(mGenericVertexAttrib0Data, kDefaultGenericVertexAttribData,
782            sizeof(mGenericVertexAttrib0Data));
783 
784     mFakeVertexAttrib0BufferObject = 0;
785 
786     return true;
787 }
788 
789 bool
ValidateFramebufferTarget(GLenum target,const char * const info)790 WebGLContext::ValidateFramebufferTarget(GLenum target,
791                                         const char* const info)
792 {
793     bool isValid = true;
794     switch (target) {
795     case LOCAL_GL_FRAMEBUFFER:
796         break;
797 
798     case LOCAL_GL_DRAW_FRAMEBUFFER:
799     case LOCAL_GL_READ_FRAMEBUFFER:
800         isValid = IsWebGL2();
801         break;
802 
803     default:
804         isValid = false;
805         break;
806     }
807 
808     if (MOZ_LIKELY(isValid)) {
809         return true;
810     }
811 
812     ErrorInvalidEnumArg(info, "target", target);
813     return false;
814 }
815 
816 } // namespace mozilla
817