summaryrefslogtreecommitdiffstats
path: root/dom/canvas/WebGLContextValidate.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/canvas/WebGLContextValidate.cpp')
-rw-r--r--dom/canvas/WebGLContextValidate.cpp632
1 files changed, 632 insertions, 0 deletions
diff --git a/dom/canvas/WebGLContextValidate.cpp b/dom/canvas/WebGLContextValidate.cpp
new file mode 100644
index 0000000000..f01ed06a36
--- /dev/null
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -0,0 +1,632 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WebGLContext.h"
+
+#include <algorithm>
+#include "GLSLANG/ShaderLang.h"
+#include "CanvasUtils.h"
+#include "gfxEnv.h"
+#include "GLContext.h"
+#include "jsfriendapi.h"
+#include "mozilla/CheckedInt.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/StaticPrefs_webgl.h"
+#include "nsPrintfCString.h"
+#include "WebGLBuffer.h"
+#include "WebGLContextUtils.h"
+#include "WebGLFramebuffer.h"
+#include "WebGLProgram.h"
+#include "WebGLRenderbuffer.h"
+#include "WebGLSampler.h"
+#include "WebGLShader.h"
+#include "WebGLTexture.h"
+#include "WebGLValidateStrings.h"
+#include "WebGLVertexArray.h"
+
+#if defined(MOZ_WIDGET_COCOA)
+# include "nsCocoaFeatures.h"
+#endif
+
+////////////////////
+// Minimum value constants defined in GLES 2.0.25 $6.2 "State Tables":
+const uint32_t kMinMaxVertexAttribs = 8; // Page 164
+const uint32_t kMinMaxVertexUniformVectors = 128; // Page 164
+const uint32_t kMinMaxFragmentUniformVectors = 16; // Page 164
+const uint32_t kMinMaxVaryingVectors = 8; // Page 164
+
+const uint32_t kMinMaxVertexTextureImageUnits = 0; // Page 164
+const uint32_t kMinMaxFragmentTextureImageUnits = 8; // Page 164
+const uint32_t kMinMaxCombinedTextureImageUnits = 8; // Page 164
+
+const uint32_t kMinMaxDrawBuffers = 4;
+
+// These few deviate from the spec: (The minimum values in the spec are
+// ridiculously low)
+const uint32_t kMinMaxTextureSize = 1024; // ES2 spec says `64` (p162)
+const uint32_t kMinMaxCubeMapTextureSize = 512; // ES2 spec says `16` (p162)
+const uint32_t kMinMaxRenderbufferSize = 1024; // ES2 spec says `1` (p164)
+
+// Minimum value constants defined in GLES 3.0.4 $6.2 "State Tables":
+const uint32_t kMinMax3DTextureSize = 256;
+const uint32_t kMinMaxArrayTextureLayers = 256;
+
+////////////////////
+// "Common" but usable values to avoid WebGL fingerprinting:
+const uint32_t kCommonMaxTextureSize = 2048;
+const uint32_t kCommonMaxCubeMapTextureSize = 2048;
+const uint32_t kCommonMaxRenderbufferSize = 2048;
+
+const uint32_t kCommonMaxVertexTextureImageUnits = 8;
+const uint32_t kCommonMaxFragmentTextureImageUnits = 8;
+const uint32_t kCommonMaxCombinedTextureImageUnits = 16;
+
+const uint32_t kCommonMaxVertexAttribs = 16;
+const uint32_t kCommonMaxVertexUniformVectors = 256;
+const uint32_t kCommonMaxFragmentUniformVectors = 224;
+const uint32_t kCommonMaxVaryingVectors = 8;
+
+const uint32_t kCommonMaxViewportDims = 4096;
+
+// The following ranges came from a 2013 Moto E and an old macbook.
+const float kCommonAliasedPointSizeRangeMin = 1;
+const float kCommonAliasedPointSizeRangeMax = 63;
+const float kCommonAliasedLineWidthRangeMin = 1;
+const float kCommonAliasedLineWidthRangeMax = 1;
+
+template <class T>
+static bool RestrictCap(T* const cap, const T restrictedVal) {
+ if (*cap < restrictedVal) {
+ return false; // already too low!
+ }
+
+ *cap = restrictedVal;
+ return true;
+}
+
+////////////////////
+
+namespace mozilla {
+
+bool WebGLContext::ValidateBlendEquationEnum(GLenum mode, const char* info) {
+ switch (mode) {
+ case LOCAL_GL_FUNC_ADD:
+ case LOCAL_GL_FUNC_SUBTRACT:
+ case LOCAL_GL_FUNC_REVERSE_SUBTRACT:
+ return true;
+
+ case LOCAL_GL_MIN:
+ case LOCAL_GL_MAX:
+ if (IsWebGL2() ||
+ IsExtensionEnabled(WebGLExtensionID::EXT_blend_minmax)) {
+ return true;
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ ErrorInvalidEnumInfo(info, mode);
+ return false;
+}
+
+bool WebGLContext::ValidateBlendFuncEnumsCompatibility(GLenum sfactor,
+ GLenum dfactor,
+ const char* info) {
+ bool sfactorIsConstantColor = sfactor == LOCAL_GL_CONSTANT_COLOR ||
+ sfactor == LOCAL_GL_ONE_MINUS_CONSTANT_COLOR;
+ bool sfactorIsConstantAlpha = sfactor == LOCAL_GL_CONSTANT_ALPHA ||
+ sfactor == LOCAL_GL_ONE_MINUS_CONSTANT_ALPHA;
+ bool dfactorIsConstantColor = dfactor == LOCAL_GL_CONSTANT_COLOR ||
+ dfactor == LOCAL_GL_ONE_MINUS_CONSTANT_COLOR;
+ bool dfactorIsConstantAlpha = dfactor == LOCAL_GL_CONSTANT_ALPHA ||
+ dfactor == LOCAL_GL_ONE_MINUS_CONSTANT_ALPHA;
+ if ((sfactorIsConstantColor && dfactorIsConstantAlpha) ||
+ (dfactorIsConstantColor && sfactorIsConstantAlpha)) {
+ ErrorInvalidOperation(
+ "%s are mutually incompatible, see section 6.8 in"
+ " the WebGL 1.0 spec",
+ info);
+ return false;
+ }
+
+ return true;
+}
+
+bool WebGLContext::ValidateStencilOpEnum(GLenum action, const char* info) {
+ switch (action) {
+ case LOCAL_GL_KEEP:
+ case LOCAL_GL_ZERO:
+ case LOCAL_GL_REPLACE:
+ case LOCAL_GL_INCR:
+ case LOCAL_GL_INCR_WRAP:
+ case LOCAL_GL_DECR:
+ case LOCAL_GL_DECR_WRAP:
+ case LOCAL_GL_INVERT:
+ return true;
+
+ default:
+ ErrorInvalidEnumInfo(info, action);
+ return false;
+ }
+}
+
+bool WebGLContext::ValidateFaceEnum(const GLenum face) {
+ switch (face) {
+ case LOCAL_GL_FRONT:
+ case LOCAL_GL_BACK:
+ case LOCAL_GL_FRONT_AND_BACK:
+ return true;
+
+ default:
+ ErrorInvalidEnumInfo("face", face);
+ return false;
+ }
+}
+
+bool WebGLContext::ValidateAttribArraySetter(uint32_t setterElemSize,
+ uint32_t arrayLength) {
+ if (IsContextLost()) return false;
+
+ if (arrayLength < setterElemSize) {
+ ErrorInvalidValue("Array must have >= %d elements.", setterElemSize);
+ return false;
+ }
+
+ return true;
+}
+
+// ---------------------
+
+static webgl::Limits MakeLimits(const WebGLContext& webgl) {
+ webgl::Limits limits;
+
+ gl::GLContext& gl = *webgl.GL();
+
+ // -
+
+ for (const auto i : IntegerRange(UnderlyingValue(WebGLExtensionID::Max))) {
+ const auto ext = WebGLExtensionID(i);
+ limits.supportedExtensions[ext] = webgl.IsExtensionSupported(ext);
+ }
+
+ // -
+ // WebGL 1
+
+ // Note: GL_MAX_TEXTURE_UNITS is fixed at 4 for most desktop hardware,
+ // even though the hardware supports much more. The
+ // GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS value is the accurate value.
+ gl.GetUIntegerv(LOCAL_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS,
+ &limits.maxTexUnits);
+ limits.maxTexUnits = std::min(
+ limits.maxTexUnits, uint32_t{UINT8_MAX}); // We want to use uint8_t.
+
+ gl.GetUIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, &limits.maxTex2dSize);
+ gl.GetUIntegerv(LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE, &limits.maxTexCubeSize);
+ gl.GetUIntegerv(LOCAL_GL_MAX_VERTEX_ATTRIBS, &limits.maxVertexAttribs);
+
+ auto dims = std::array<uint32_t, 2>{};
+ gl.GetUIntegerv(LOCAL_GL_MAX_VIEWPORT_DIMS, dims.data());
+ limits.maxViewportDim = std::min(dims[0], dims[1]);
+
+ if (!gl.IsCoreProfile()) {
+ gl.fGetFloatv(LOCAL_GL_ALIASED_LINE_WIDTH_RANGE,
+ limits.lineWidthRange.data());
+ }
+
+ {
+ const GLenum driverPName = gl.IsCoreProfile()
+ ? LOCAL_GL_POINT_SIZE_RANGE
+ : LOCAL_GL_ALIASED_POINT_SIZE_RANGE;
+ gl.fGetFloatv(driverPName, limits.pointSizeRange.data());
+ }
+
+ if (webgl.IsWebGL2()) {
+ gl.GetUIntegerv(LOCAL_GL_MAX_ARRAY_TEXTURE_LAYERS,
+ &limits.maxTexArrayLayers);
+ gl.GetUIntegerv(LOCAL_GL_MAX_3D_TEXTURE_SIZE, &limits.maxTex3dSize);
+ gl.GetUIntegerv(LOCAL_GL_MAX_UNIFORM_BUFFER_BINDINGS,
+ &limits.maxUniformBufferBindings);
+ gl.GetUIntegerv(LOCAL_GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT,
+ &limits.uniformBufferOffsetAlignment);
+ }
+
+ if (limits.supportedExtensions
+ [WebGLExtensionID::WEBGL_compressed_texture_astc]) {
+ limits.astcHdr = gl.IsExtensionSupported(
+ gl::GLContext::KHR_texture_compression_astc_hdr);
+ }
+
+ if (webgl.IsWebGL2() ||
+ limits.supportedExtensions[WebGLExtensionID::WEBGL_draw_buffers]) {
+ gl.GetUIntegerv(LOCAL_GL_MAX_DRAW_BUFFERS, &limits.maxColorDrawBuffers);
+ }
+
+ if (limits.supportedExtensions[WebGLExtensionID::EXT_disjoint_timer_query]) {
+ gl.fGetQueryiv(LOCAL_GL_TIME_ELAPSED_EXT, LOCAL_GL_QUERY_COUNTER_BITS,
+ (int32_t*)&limits.queryCounterBitsTimeElapsed);
+ gl.fGetQueryiv(LOCAL_GL_TIMESTAMP_EXT, LOCAL_GL_QUERY_COUNTER_BITS,
+ (int32_t*)&limits.queryCounterBitsTimestamp);
+ }
+
+ if (limits.supportedExtensions[WebGLExtensionID::OVR_multiview2]) {
+ gl.GetUIntegerv(LOCAL_GL_MAX_VIEWS_OVR, &limits.maxMultiviewLayers);
+ }
+
+ return limits;
+}
+
+bool WebGLContext::InitAndValidateGL(FailureReason* const out_failReason) {
+ MOZ_RELEASE_ASSERT(gl, "GFX: GL not initialized");
+
+ // Unconditionally create a new format usage authority. This is
+ // important when restoring contexts and extensions need to add
+ // formats back into the authority.
+ mFormatUsage = CreateFormatUsage(gl);
+ if (!mFormatUsage) {
+ *out_failReason = {"FEATURE_FAILURE_WEBGL_FORMAT",
+ "Failed to create mFormatUsage."};
+ return false;
+ }
+
+ GLenum error = gl->fGetError();
+ if (error != LOCAL_GL_NO_ERROR) {
+ const nsPrintfCString reason(
+ "GL error 0x%x occurred during OpenGL context"
+ " initialization, before WebGL initialization!",
+ error);
+ *out_failReason = {"FEATURE_FAILURE_WEBGL_GLERR_1", reason};
+ return false;
+ }
+
+ mLoseContextOnMemoryPressure =
+ StaticPrefs::webgl_lose_context_on_memory_pressure();
+ mCanLoseContextInForeground =
+ StaticPrefs::webgl_can_lose_context_in_foreground();
+
+ /*
+ // Technically, we should be setting mStencil[...] values to
+ // `allOnes`, but either ANGLE breaks or the SGX540s on Try break.
+ GLuint stencilBits = 0;
+ gl->GetUIntegerv(LOCAL_GL_STENCIL_BITS, &stencilBits);
+ GLuint allOnes = ~(UINT32_MAX << stencilBits);
+ mStencilValueMaskFront = allOnes;
+ mStencilValueMaskBack = allOnes;
+ mStencilWriteMaskFront = allOnes;
+ mStencilWriteMaskBack = allOnes;
+ */
+
+ gl->GetUIntegerv(LOCAL_GL_STENCIL_VALUE_MASK, &mStencilValueMaskFront);
+ gl->GetUIntegerv(LOCAL_GL_STENCIL_BACK_VALUE_MASK, &mStencilValueMaskBack);
+ gl->GetUIntegerv(LOCAL_GL_STENCIL_WRITEMASK, &mStencilWriteMaskFront);
+ gl->GetUIntegerv(LOCAL_GL_STENCIL_BACK_WRITEMASK, &mStencilWriteMaskBack);
+
+ AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_VALUE_MASK,
+ mStencilValueMaskFront);
+ AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_BACK_VALUE_MASK,
+ mStencilValueMaskBack);
+ AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_WRITEMASK,
+ mStencilWriteMaskFront);
+ AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_BACK_WRITEMASK,
+ mStencilWriteMaskBack);
+
+ // Bindings, etc.
+
+ mBound2DTextures.Clear();
+ mBoundCubeMapTextures.Clear();
+ mBound3DTextures.Clear();
+ mBound2DArrayTextures.Clear();
+ mBoundSamplers.Clear();
+
+ mBoundArrayBuffer = nullptr;
+ mCurrentProgram = nullptr;
+
+ mBoundDrawFramebuffer = nullptr;
+ mBoundReadFramebuffer = nullptr;
+
+ // -----------------------
+
+ auto limits = MakeLimits(*this);
+
+ // -
+
+ if (limits.maxVertexAttribs < 8) {
+ const nsPrintfCString reason("GL_MAX_VERTEX_ATTRIBS: %d is < 8!",
+ limits.maxVertexAttribs);
+ *out_failReason = {"FEATURE_FAILURE_WEBGL_V_ATRB", reason};
+ return false;
+ }
+
+ if (limits.maxTexUnits < 8) {
+ const nsPrintfCString reason(
+ "GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: %u is < 8!", limits.maxTexUnits);
+ *out_failReason = {"FEATURE_FAILURE_WEBGL_T_UNIT", reason};
+ return false;
+ }
+
+ mBound2DTextures.SetLength(limits.maxTexUnits);
+ mBoundCubeMapTextures.SetLength(limits.maxTexUnits);
+ mBound3DTextures.SetLength(limits.maxTexUnits);
+ mBound2DArrayTextures.SetLength(limits.maxTexUnits);
+ mBoundSamplers.SetLength(limits.maxTexUnits);
+
+ ////////////////
+
+ gl->GetUIntegerv(LOCAL_GL_MAX_RENDERBUFFER_SIZE, &mGLMaxRenderbufferSize);
+ gl->GetUIntegerv(LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS,
+ &mGLMaxFragmentTextureImageUnits);
+ gl->GetUIntegerv(LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS,
+ &mGLMaxVertexTextureImageUnits);
+
+ ////////////////
+
+ if (gl->IsGLES()) {
+ mGLMaxFragmentUniformVectors =
+ gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_FRAGMENT_UNIFORM_VECTORS);
+ mGLMaxVertexUniformVectors =
+ gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_VERTEX_UNIFORM_VECTORS);
+ if (gl->Version() >= 300) {
+ mGLMaxVertexOutputVectors =
+ gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_VERTEX_OUTPUT_COMPONENTS) / 4;
+ mGLMaxFragmentInputVectors =
+ gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_FRAGMENT_INPUT_COMPONENTS) / 4;
+ } else {
+ mGLMaxFragmentInputVectors =
+ gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_VARYING_VECTORS);
+ mGLMaxVertexOutputVectors = mGLMaxFragmentInputVectors;
+ }
+ } else {
+ mGLMaxFragmentUniformVectors =
+ gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS) / 4;
+ mGLMaxVertexUniformVectors =
+ gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_VERTEX_UNIFORM_COMPONENTS) / 4;
+
+ if (gl->Version() >= 320) {
+ mGLMaxVertexOutputVectors =
+ gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_VERTEX_OUTPUT_COMPONENTS) / 4;
+ mGLMaxFragmentInputVectors =
+ gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_FRAGMENT_INPUT_COMPONENTS) / 4;
+ } else {
+ // Same enum val as GL2's GL_MAX_VARYING_FLOATS.
+ mGLMaxFragmentInputVectors =
+ gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_VARYING_COMPONENTS) / 4;
+ mGLMaxVertexOutputVectors = mGLMaxFragmentInputVectors;
+ }
+ }
+
+ ////////////////
+
+ if (StaticPrefs::webgl_min_capability_mode()) {
+ bool ok = true;
+
+ ok &= RestrictCap(&mGLMaxVertexTextureImageUnits,
+ kMinMaxVertexTextureImageUnits);
+ ok &= RestrictCap(&mGLMaxFragmentTextureImageUnits,
+ kMinMaxFragmentTextureImageUnits);
+ ok &= RestrictCap(&limits.maxTexUnits, kMinMaxCombinedTextureImageUnits);
+
+ ok &= RestrictCap(&limits.maxVertexAttribs, kMinMaxVertexAttribs);
+ ok &= RestrictCap(&mGLMaxVertexUniformVectors, kMinMaxVertexUniformVectors);
+ ok &= RestrictCap(&mGLMaxFragmentUniformVectors,
+ kMinMaxFragmentUniformVectors);
+ ok &= RestrictCap(&mGLMaxVertexOutputVectors, kMinMaxVaryingVectors);
+ ok &= RestrictCap(&mGLMaxFragmentInputVectors, kMinMaxVaryingVectors);
+
+ ok &= RestrictCap(&limits.maxColorDrawBuffers, kMinMaxDrawBuffers);
+
+ ok &= RestrictCap(&limits.maxTex2dSize, kMinMaxTextureSize);
+ ok &= RestrictCap(&limits.maxTexCubeSize, kMinMaxCubeMapTextureSize);
+ ok &= RestrictCap(&limits.maxTex3dSize, kMinMax3DTextureSize);
+
+ ok &= RestrictCap(&limits.maxTexArrayLayers, kMinMaxArrayTextureLayers);
+ ok &= RestrictCap(&mGLMaxRenderbufferSize, kMinMaxRenderbufferSize);
+
+ if (!ok) {
+ GenerateWarning("Unable to restrict WebGL limits to minimums.");
+ return false;
+ }
+
+ mDisableFragHighP = true;
+ } else if (mResistFingerprinting) {
+ bool ok = true;
+
+ ok &= RestrictCap(&limits.maxTex2dSize, kCommonMaxTextureSize);
+ ok &= RestrictCap(&limits.maxTexCubeSize, kCommonMaxCubeMapTextureSize);
+ ok &= RestrictCap(&mGLMaxRenderbufferSize, kCommonMaxRenderbufferSize);
+
+ ok &= RestrictCap(&mGLMaxVertexTextureImageUnits,
+ kCommonMaxVertexTextureImageUnits);
+ ok &= RestrictCap(&mGLMaxFragmentTextureImageUnits,
+ kCommonMaxFragmentTextureImageUnits);
+ ok &= RestrictCap(&limits.maxTexUnits, kCommonMaxCombinedTextureImageUnits);
+
+ ok &= RestrictCap(&limits.maxVertexAttribs, kCommonMaxVertexAttribs);
+ ok &= RestrictCap(&mGLMaxVertexUniformVectors,
+ kCommonMaxVertexUniformVectors);
+ ok &= RestrictCap(&mGLMaxFragmentUniformVectors,
+ kCommonMaxFragmentUniformVectors);
+ ok &= RestrictCap(&mGLMaxVertexOutputVectors, kCommonMaxVaryingVectors);
+ ok &= RestrictCap(&mGLMaxFragmentInputVectors, kCommonMaxVaryingVectors);
+
+ if (limits.lineWidthRange[0] <= kCommonAliasedLineWidthRangeMin) {
+ limits.lineWidthRange[0] = kCommonAliasedLineWidthRangeMin;
+ } else {
+ ok = false;
+ }
+ if (limits.pointSizeRange[0] <= kCommonAliasedPointSizeRangeMin) {
+ limits.pointSizeRange[0] = kCommonAliasedPointSizeRangeMin;
+ } else {
+ ok = false;
+ }
+
+ ok &=
+ RestrictCap(&limits.lineWidthRange[1], kCommonAliasedLineWidthRangeMax);
+ ok &=
+ RestrictCap(&limits.pointSizeRange[1], kCommonAliasedPointSizeRangeMax);
+ ok &= RestrictCap(&limits.maxViewportDim, kCommonMaxViewportDims);
+
+ if (!ok) {
+ GenerateWarning(
+ "Unable to restrict WebGL limits in order to resist fingerprinting");
+ return false;
+ }
+ }
+
+ mLimits = Some(limits);
+
+ ////////////////
+
+ if (gl->IsCompatibilityProfile()) {
+ gl->fEnable(LOCAL_GL_POINT_SPRITE);
+ }
+
+ if (!gl->IsGLES()) {
+ gl->fEnable(LOCAL_GL_PROGRAM_POINT_SIZE);
+ }
+
+#ifdef XP_MACOSX
+ if (gl->WorkAroundDriverBugs() && gl->Vendor() == gl::GLVendor::ATI &&
+ !nsCocoaFeatures::IsAtLeastVersion(10, 9)) {
+ // The Mac ATI driver, in all known OSX version up to and including
+ // 10.8, renders points sprites upside-down. (Apple bug 11778921)
+ gl->fPointParameterf(LOCAL_GL_POINT_SPRITE_COORD_ORIGIN,
+ LOCAL_GL_LOWER_LEFT);
+ }
+#endif
+
+ if (gl->IsSupported(gl::GLFeature::seamless_cube_map_opt_in)) {
+ gl->fEnable(LOCAL_GL_TEXTURE_CUBE_MAP_SEAMLESS);
+ }
+
+ // initialize shader translator
+ if (!sh::Initialize()) {
+ *out_failReason = {"FEATURE_FAILURE_WEBGL_GLSL",
+ "GLSL translator initialization failed!"};
+ return false;
+ }
+
+ // Mesa can only be detected with the GL_VERSION string, of the form
+ // "2.1 Mesa 7.11.0"
+ const char* versionStr = (const char*)(gl->fGetString(LOCAL_GL_VERSION));
+ mIsMesa = strstr(versionStr, "Mesa");
+
+ // Notice that the point of calling fGetError here is not only to check for
+ // errors, but also to reset the error flags so that a subsequent WebGL
+ // getError call will give the correct result.
+ error = gl->fGetError();
+ if (error != LOCAL_GL_NO_ERROR) {
+ const nsPrintfCString reason(
+ "GL error 0x%x occurred during WebGL context"
+ " initialization!",
+ error);
+ *out_failReason = {"FEATURE_FAILURE_WEBGL_GLERR_2", reason};
+ return false;
+ }
+
+ if (IsWebGL2() && !InitWebGL2(out_failReason)) {
+ // Todo: Bug 898404: Only allow WebGL2 on GL>=3.0 on desktop GL.
+ return false;
+ }
+
+ // OpenGL core profiles remove the default VAO object from version
+ // 4.0.0. We create a default VAO for all core profiles,
+ // regardless of version.
+ //
+ // GL Spec 4.0.0:
+ // (https://www.opengl.org/registry/doc/glspec40.core.20100311.pdf)
+ // in Section E.2.2 "Removed Features", pg 397: "[...] The default
+ // vertex array object (the name zero) is also deprecated. [...]"
+ mDefaultVertexArray = WebGLVertexArray::Create(this);
+ mDefaultVertexArray->BindVertexArray();
+
+ mPrimRestartTypeBytes = 0;
+
+ // -
+
+ mGenericVertexAttribTypes.assign(limits.maxVertexAttribs,
+ webgl::AttribBaseType::Float);
+ mGenericVertexAttribTypeInvalidator.InvalidateCaches();
+
+ static const float kDefaultGenericVertexAttribData[4] = {0, 0, 0, 1};
+ memcpy(mGenericVertexAttrib0Data, kDefaultGenericVertexAttribData,
+ sizeof(mGenericVertexAttrib0Data));
+
+ mFakeVertexAttrib0BufferObject = 0;
+
+ mNeedsLegacyVertexAttrib0Handling = gl->IsCompatibilityProfile();
+ if (gl->WorkAroundDriverBugs() && kIsMacOS) {
+ // Failures in conformance/attribs/gl-disabled-vertex-attrib.
+ // Even in Core profiles on NV. Sigh.
+ mNeedsLegacyVertexAttrib0Handling |= (gl->Vendor() == gl::GLVendor::NVIDIA);
+
+ mBug_DrawArraysInstancedUserAttribFetchAffectedByFirst |=
+ (gl->Vendor() == gl::GLVendor::Intel);
+
+ // Failures for programs with no attribs:
+ // conformance/attribs/gl-vertex-attrib-unconsumed-out-of-bounds.html
+ mMaybeNeedsLegacyVertexAttrib0Handling = true;
+ }
+ mMaybeNeedsLegacyVertexAttrib0Handling |= mNeedsLegacyVertexAttrib0Handling;
+
+ if (const auto& env =
+ gfxEnv::MOZ_WEBGL_WORKAROUND_FIRST_AFFECTS_INSTANCE_ID()) {
+ const auto was = mBug_DrawArraysInstancedUserAttribFetchAffectedByFirst;
+ mBug_DrawArraysInstancedUserAttribFetchAffectedByFirst =
+ (env.as_str != "0");
+ printf_stderr(
+ "mBug_DrawArraysInstancedUserAttribFetchAffectedByFirst: %i -> %i\n",
+ int(was), int(mBug_DrawArraysInstancedUserAttribFetchAffectedByFirst));
+ }
+
+ // -
+
+ mNeedsIndexValidation =
+ !gl->IsSupported(gl::GLFeature::robust_buffer_access_behavior);
+ switch (StaticPrefs::webgl_force_index_validation()) {
+ case -1:
+ mNeedsIndexValidation = false;
+ break;
+ case 1:
+ mNeedsIndexValidation = true;
+ break;
+ default:
+ MOZ_ASSERT(StaticPrefs::webgl_force_index_validation() == 0);
+ break;
+ }
+
+ for (auto& cur : mExtensions) {
+ cur = {};
+ }
+
+ return true;
+}
+
+bool WebGLContext::ValidateFramebufferTarget(GLenum target) const {
+ bool isValid = true;
+ switch (target) {
+ case LOCAL_GL_FRAMEBUFFER:
+ break;
+
+ case LOCAL_GL_DRAW_FRAMEBUFFER:
+ case LOCAL_GL_READ_FRAMEBUFFER:
+ isValid = IsWebGL2();
+ break;
+
+ default:
+ isValid = false;
+ break;
+ }
+
+ if (MOZ_LIKELY(isValid)) {
+ return true;
+ }
+
+ ErrorInvalidEnumArg("target", target);
+ return false;
+}
+
+} // namespace mozilla