diff options
Diffstat (limited to 'dom/canvas/WebGLContextValidate.cpp')
-rw-r--r-- | dom/canvas/WebGLContextValidate.cpp | 632 |
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 |