diff options
Diffstat (limited to 'dom/canvas/WebGLContextValidate.cpp')
-rw-r--r-- | dom/canvas/WebGLContextValidate.cpp | 816 |
1 files changed, 816 insertions, 0 deletions
diff --git a/dom/canvas/WebGLContextValidate.cpp b/dom/canvas/WebGLContextValidate.cpp new file mode 100644 index 000000000..ebf0aa8c2 --- /dev/null +++ b/dom/canvas/WebGLContextValidate.cpp @@ -0,0 +1,816 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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 "angle/ShaderLang.h" +#include "CanvasUtils.h" +#include "gfxPrefs.h" +#include "GLContext.h" +#include "jsfriendapi.h" +#include "mozilla/CheckedInt.h" +#include "mozilla/Preferences.h" +#include "mozilla/Services.h" +#include "nsIObserverService.h" +#include "nsPrintfCString.h" +#include "WebGLActiveInfo.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 "WebGLUniformLocation.h" +#include "WebGLValidateStrings.h" +#include "WebGLVertexArray.h" +#include "WebGLVertexAttribData.h" + +#if defined(MOZ_WIDGET_COCOA) +#include "nsCocoaFeatures.h" +#endif + +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::ValidateBlendFuncDstEnum(GLenum factor, const char* info) +{ + switch (factor) { + case LOCAL_GL_ZERO: + case LOCAL_GL_ONE: + case LOCAL_GL_SRC_COLOR: + case LOCAL_GL_ONE_MINUS_SRC_COLOR: + case LOCAL_GL_DST_COLOR: + case LOCAL_GL_ONE_MINUS_DST_COLOR: + case LOCAL_GL_SRC_ALPHA: + case LOCAL_GL_ONE_MINUS_SRC_ALPHA: + case LOCAL_GL_DST_ALPHA: + case LOCAL_GL_ONE_MINUS_DST_ALPHA: + case LOCAL_GL_CONSTANT_COLOR: + case LOCAL_GL_ONE_MINUS_CONSTANT_COLOR: + case LOCAL_GL_CONSTANT_ALPHA: + case LOCAL_GL_ONE_MINUS_CONSTANT_ALPHA: + return true; + + default: + ErrorInvalidEnumInfo(info, factor); + return false; + } +} + +bool +WebGLContext::ValidateBlendFuncSrcEnum(GLenum factor, const char* info) +{ + if (factor == LOCAL_GL_SRC_ALPHA_SATURATE) + return true; + + return ValidateBlendFuncDstEnum(factor, info); +} + +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::ValidateComparisonEnum(GLenum target, const char* info) +{ + switch (target) { + case LOCAL_GL_NEVER: + case LOCAL_GL_LESS: + case LOCAL_GL_LEQUAL: + case LOCAL_GL_GREATER: + case LOCAL_GL_GEQUAL: + case LOCAL_GL_EQUAL: + case LOCAL_GL_NOTEQUAL: + case LOCAL_GL_ALWAYS: + return true; + + default: + ErrorInvalidEnumInfo(info, target); + return false; + } +} + +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(GLenum face, const char* info) +{ + switch (face) { + case LOCAL_GL_FRONT: + case LOCAL_GL_BACK: + case LOCAL_GL_FRONT_AND_BACK: + return true; + + default: + ErrorInvalidEnumInfo(info, face); + return false; + } +} + +bool +WebGLContext::ValidateDrawModeEnum(GLenum mode, const char* info) +{ + switch (mode) { + case LOCAL_GL_TRIANGLES: + case LOCAL_GL_TRIANGLE_STRIP: + case LOCAL_GL_TRIANGLE_FAN: + case LOCAL_GL_POINTS: + case LOCAL_GL_LINE_STRIP: + case LOCAL_GL_LINE_LOOP: + case LOCAL_GL_LINES: + return true; + + default: + ErrorInvalidEnumInfo(info, mode); + return false; + } +} + +bool +WebGLContext::ValidateUniformLocation(WebGLUniformLocation* loc, const char* funcName) +{ + /* GLES 2.0.25, p38: + * If the value of location is -1, the Uniform* commands will silently + * ignore the data passed in, and the current uniform values will not be + * changed. + */ + if (!loc) + return false; + + if (!ValidateObjectAllowDeleted(funcName, *loc)) + return false; + + if (!mCurrentProgram) { + ErrorInvalidOperation("%s: No program is currently bound.", funcName); + return false; + } + + return loc->ValidateForProgram(mCurrentProgram, funcName); +} + +bool +WebGLContext::ValidateAttribArraySetter(const char* name, uint32_t setterElemSize, + uint32_t arrayLength) +{ + if (IsContextLost()) + return false; + + if (arrayLength < setterElemSize) { + ErrorInvalidValue("%s: Array must have >= %d elements.", name, + setterElemSize); + return false; + } + + return true; +} + +bool +WebGLContext::ValidateUniformSetter(WebGLUniformLocation* loc, + uint8_t setterElemSize, GLenum setterType, + const char* funcName) +{ + if (IsContextLost()) + return false; + + if (!ValidateUniformLocation(loc, funcName)) + return false; + + if (!loc->ValidateSizeAndType(setterElemSize, setterType, funcName)) + return false; + + return true; +} + +bool +WebGLContext::ValidateUniformArraySetter(WebGLUniformLocation* loc, + uint8_t setterElemSize, + GLenum setterType, + uint32_t setterArraySize, + const char* funcName, + uint32_t* const out_numElementsToUpload) +{ + if (IsContextLost()) + return false; + + if (!ValidateUniformLocation(loc, funcName)) + return false; + + if (!loc->ValidateSizeAndType(setterElemSize, setterType, funcName)) + return false; + + if (!loc->ValidateArrayLength(setterElemSize, setterArraySize, funcName)) + return false; + + const auto& elemCount = loc->mInfo->mActiveInfo->mElemCount; + MOZ_ASSERT(elemCount > loc->mArrayIndex); + const uint32_t uniformElemCount = elemCount - loc->mArrayIndex; + + *out_numElementsToUpload = std::min(uniformElemCount, + setterArraySize / setterElemSize); + return true; +} + +bool +WebGLContext::ValidateUniformMatrixArraySetter(WebGLUniformLocation* loc, + uint8_t setterCols, + uint8_t setterRows, + GLenum setterType, + uint32_t setterArraySize, + bool setterTranspose, + const char* funcName, + uint32_t* const out_numElementsToUpload) +{ + const uint8_t setterElemSize = setterCols * setterRows; + + if (IsContextLost()) + return false; + + if (!ValidateUniformLocation(loc, funcName)) + return false; + + if (!loc->ValidateSizeAndType(setterElemSize, setterType, funcName)) + return false; + + if (!loc->ValidateArrayLength(setterElemSize, setterArraySize, funcName)) + return false; + + if (!ValidateUniformMatrixTranspose(setterTranspose, funcName)) + return false; + + const auto& elemCount = loc->mInfo->mActiveInfo->mElemCount; + MOZ_ASSERT(elemCount > loc->mArrayIndex); + const uint32_t uniformElemCount = elemCount - loc->mArrayIndex; + + *out_numElementsToUpload = std::min(uniformElemCount, + setterArraySize / setterElemSize); + return true; +} + +bool +WebGLContext::ValidateAttribIndex(GLuint index, const char* info) +{ + bool valid = (index < MaxVertexAttribs()); + + if (!valid) { + if (index == GLuint(-1)) { + ErrorInvalidValue("%s: -1 is not a valid `index`. This value" + " probably comes from a getAttribLocation()" + " call, where this return value -1 means" + " that the passed name didn't correspond to" + " an active attribute in the specified" + " program.", info); + } else { + ErrorInvalidValue("%s: `index` must be less than" + " MAX_VERTEX_ATTRIBS.", info); + } + } + + return valid; +} + +bool +WebGLContext::ValidateAttribPointer(bool integerMode, GLuint index, GLint size, GLenum type, + WebGLboolean normalized, GLsizei stride, + WebGLintptr byteOffset, const char* info) +{ + WebGLBuffer* buffer = mBoundArrayBuffer; + if (!buffer) { + ErrorInvalidOperation("%s: must have valid GL_ARRAY_BUFFER binding", info); + return false; + } + + uint32_t requiredAlignment = 0; + if (!ValidateAttribPointerType(integerMode, type, &requiredAlignment, info)) + return false; + + // requiredAlignment should always be a power of two + MOZ_ASSERT(IsPowerOfTwo(requiredAlignment)); + GLsizei requiredAlignmentMask = requiredAlignment - 1; + + if (size < 1 || size > 4) { + ErrorInvalidValue("%s: invalid element size", info); + return false; + } + + switch (type) { + case LOCAL_GL_INT_2_10_10_10_REV: + case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV: + if (size != 4) { + ErrorInvalidOperation("%s: size must be 4 for this type.", info); + return false; + } + break; + } + + // see WebGL spec section 6.6 "Vertex Attribute Data Stride" + if (stride < 0 || stride > 255) { + ErrorInvalidValue("%s: negative or too large stride", info); + return false; + } + + if (byteOffset < 0) { + ErrorInvalidValue("%s: negative offset", info); + return false; + } + + if (stride & requiredAlignmentMask) { + ErrorInvalidOperation("%s: stride doesn't satisfy the alignment " + "requirement of given type", info); + return false; + } + + if (byteOffset & requiredAlignmentMask) { + ErrorInvalidOperation("%s: byteOffset doesn't satisfy the alignment " + "requirement of given type", info); + return false; + } + + return true; +} + +bool +WebGLContext::ValidateStencilParamsForDrawCall() +{ + const char msg[] = "%s set different front and back stencil %s. Drawing in" + " this configuration is not allowed."; + + if (mStencilRefFront != mStencilRefBack) { + ErrorInvalidOperation(msg, "stencilFuncSeparate", "reference values"); + return false; + } + + if (mStencilValueMaskFront != mStencilValueMaskBack) { + ErrorInvalidOperation(msg, "stencilFuncSeparate", "value masks"); + return false; + } + + if (mStencilWriteMaskFront != mStencilWriteMaskBack) { + ErrorInvalidOperation(msg, "stencilMaskSeparate", "write masks"); + return false; + } + + return true; +} + +static inline int32_t +FloorPOT(int32_t x) +{ + MOZ_ASSERT(x > 0); + int32_t pot = 1; + while (pot < 0x40000000) { + if (x < pot*2) + break; + pot *= 2; + } + return pot; +} + +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; + } + + mMinCapability = gfxPrefs::WebGLMinCapabilityMode(); + mDisableExtensions = gfxPrefs::WebGLDisableExtensions(); + mLoseContextOnMemoryPressure = gfxPrefs::WebGLLoseContextOnMemoryPressure(); + mCanLoseContextInForeground = gfxPrefs::WebGLCanLoseContextInForeground(); + mRestoreWhenVisible = gfxPrefs::WebGLRestoreWhenVisible(); + + if (MinCapabilityMode()) + mDisableFragHighP = true; + + // These are the default values, see 6.2 State tables in the + // OpenGL ES 2.0.25 spec. + mColorWriteMask[0] = 1; + mColorWriteMask[1] = 1; + mColorWriteMask[2] = 1; + mColorWriteMask[3] = 1; + mDepthWriteMask = 1; + mColorClearValue[0] = 0.f; + mColorClearValue[1] = 0.f; + mColorClearValue[2] = 0.f; + mColorClearValue[3] = 0.f; + mDepthClearValue = 1.f; + mStencilClearValue = 0; + mStencilRefFront = 0; + mStencilRefBack = 0; + + mLineWidth = 1.0; + + /* + // 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); + + mDitherEnabled = true; + mRasterizerDiscardEnabled = false; + mScissorTestEnabled = false; + mGenerateMipmapHint = LOCAL_GL_DONT_CARE; + + // Bindings, etc. + mActiveTexture = 0; + mDefaultFB_DrawBuffer0 = LOCAL_GL_BACK; + + mEmitContextLostErrorOnce = true; + mWebGLError = LOCAL_GL_NO_ERROR; + mUnderlyingGLError = LOCAL_GL_NO_ERROR; + + mBound2DTextures.Clear(); + mBoundCubeMapTextures.Clear(); + mBound3DTextures.Clear(); + mBound2DArrayTextures.Clear(); + mBoundSamplers.Clear(); + + mBoundArrayBuffer = nullptr; + mCurrentProgram = nullptr; + + mBoundDrawFramebuffer = nullptr; + mBoundReadFramebuffer = nullptr; + mBoundRenderbuffer = nullptr; + + MakeContextCurrent(); + + if (MinCapabilityMode()) + mGLMaxVertexAttribs = MINVALUE_GL_MAX_VERTEX_ATTRIBS; + else + gl->GetUIntegerv(LOCAL_GL_MAX_VERTEX_ATTRIBS, &mGLMaxVertexAttribs); + + if (mGLMaxVertexAttribs < 8) { + const nsPrintfCString reason("GL_MAX_VERTEX_ATTRIBS: %d is < 8!", + mGLMaxVertexAttribs); + *out_failReason = { "FEATURE_FAILURE_WEBGL_V_ATRB", reason }; + return false; + } + + // 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. + if (MinCapabilityMode()) + mGLMaxTextureUnits = MINVALUE_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS; + else + mGLMaxTextureUnits = gl->GetIntAs<GLint>(LOCAL_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS); + + if (mGLMaxTextureUnits < 8) { + const nsPrintfCString reason("GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: %d is < 8!", + mGLMaxTextureUnits); + *out_failReason = { "FEATURE_FAILURE_WEBGL_T_UNIT", reason }; + return false; + } + + mBound2DTextures.SetLength(mGLMaxTextureUnits); + mBoundCubeMapTextures.SetLength(mGLMaxTextureUnits); + mBound3DTextures.SetLength(mGLMaxTextureUnits); + mBound2DArrayTextures.SetLength(mGLMaxTextureUnits); + mBoundSamplers.SetLength(mGLMaxTextureUnits); + + gl->fGetIntegerv(LOCAL_GL_MAX_VIEWPORT_DIMS, (GLint*)mImplMaxViewportDims); + + //////////////// + + if (MinCapabilityMode()) { + mImplMaxTextureSize = MINVALUE_GL_MAX_TEXTURE_SIZE; + mImplMaxCubeMapTextureSize = MINVALUE_GL_MAX_CUBE_MAP_TEXTURE_SIZE; + mImplMaxRenderbufferSize = MINVALUE_GL_MAX_RENDERBUFFER_SIZE; + + mImplMax3DTextureSize = MINVALUE_GL_MAX_3D_TEXTURE_SIZE; + mImplMaxArrayTextureLayers = MINVALUE_GL_MAX_ARRAY_TEXTURE_LAYERS; + + mGLMaxTextureImageUnits = MINVALUE_GL_MAX_TEXTURE_IMAGE_UNITS; + mGLMaxVertexTextureImageUnits = MINVALUE_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS; + } else { + gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, (GLint*)&mImplMaxTextureSize); + gl->fGetIntegerv(LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE, (GLint*)&mImplMaxCubeMapTextureSize); + gl->fGetIntegerv(LOCAL_GL_MAX_RENDERBUFFER_SIZE, (GLint*)&mImplMaxRenderbufferSize); + + if (!gl->GetPotentialInteger(LOCAL_GL_MAX_3D_TEXTURE_SIZE, (GLint*)&mImplMax3DTextureSize)) + mImplMax3DTextureSize = 0; + if (!gl->GetPotentialInteger(LOCAL_GL_MAX_ARRAY_TEXTURE_LAYERS, (GLint*)&mImplMaxArrayTextureLayers)) + mImplMaxArrayTextureLayers = 0; + + gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS, &mGLMaxTextureImageUnits); + gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &mGLMaxVertexTextureImageUnits); + } + + // If we don't support a target, its max size is 0. We should only floor-to-POT if the + // value if it's non-zero. (NB log2(0) is -Inf, so zero isn't an integer power-of-two) + const auto fnFloorPOTIfSupported = [](uint32_t& val) { + if (val) { + val = FloorPOT(val); + } + }; + + fnFloorPOTIfSupported(mImplMaxTextureSize); + fnFloorPOTIfSupported(mImplMaxCubeMapTextureSize); + fnFloorPOTIfSupported(mImplMaxRenderbufferSize); + + fnFloorPOTIfSupported(mImplMax3DTextureSize); + fnFloorPOTIfSupported(mImplMaxArrayTextureLayers); + + //////////////// + + mGLMaxColorAttachments = 1; + mGLMaxDrawBuffers = 1; + gl->GetPotentialInteger(LOCAL_GL_MAX_COLOR_ATTACHMENTS, + (GLint*)&mGLMaxColorAttachments); + gl->GetPotentialInteger(LOCAL_GL_MAX_DRAW_BUFFERS, (GLint*)&mGLMaxDrawBuffers); + + if (MinCapabilityMode()) { + mGLMaxColorAttachments = std::min(mGLMaxColorAttachments, + kMinMaxColorAttachments); + mGLMaxDrawBuffers = std::min(mGLMaxDrawBuffers, kMinMaxDrawBuffers); + } + + if (IsWebGL2()) { + mImplMaxColorAttachments = mGLMaxColorAttachments; + mImplMaxDrawBuffers = std::min(mGLMaxDrawBuffers, mImplMaxColorAttachments); + } else { + mImplMaxColorAttachments = 1; + mImplMaxDrawBuffers = 1; + } + + //////////////// + + if (MinCapabilityMode()) { + mGLMaxFragmentUniformVectors = MINVALUE_GL_MAX_FRAGMENT_UNIFORM_VECTORS; + mGLMaxVertexUniformVectors = MINVALUE_GL_MAX_VERTEX_UNIFORM_VECTORS; + mGLMaxVaryingVectors = MINVALUE_GL_MAX_VARYING_VECTORS; + } else { + if (gl->IsSupported(gl::GLFeature::ES2_compatibility)) { + gl->fGetIntegerv(LOCAL_GL_MAX_FRAGMENT_UNIFORM_VECTORS, &mGLMaxFragmentUniformVectors); + gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_UNIFORM_VECTORS, &mGLMaxVertexUniformVectors); + gl->fGetIntegerv(LOCAL_GL_MAX_VARYING_VECTORS, &mGLMaxVaryingVectors); + } else { + gl->fGetIntegerv(LOCAL_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &mGLMaxFragmentUniformVectors); + mGLMaxFragmentUniformVectors /= 4; + gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_UNIFORM_COMPONENTS, &mGLMaxVertexUniformVectors); + mGLMaxVertexUniformVectors /= 4; + + /* We are now going to try to read GL_MAX_VERTEX_OUTPUT_COMPONENTS + * and GL_MAX_FRAGMENT_INPUT_COMPONENTS, however these constants + * only entered the OpenGL standard at OpenGL 3.2. So we will try + * reading, and check OpenGL error for INVALID_ENUM. + * + * On the public_webgl list, "problematic GetParameter pnames" + * thread, the following formula was given: + * maxVaryingVectors = min(GL_MAX_VERTEX_OUTPUT_COMPONENTS, + * GL_MAX_FRAGMENT_INPUT_COMPONENTS) / 4 + */ + GLint maxVertexOutputComponents = 0; + GLint maxFragmentInputComponents = 0; + + const bool ok = (gl->GetPotentialInteger(LOCAL_GL_MAX_VERTEX_OUTPUT_COMPONENTS, + &maxVertexOutputComponents) && + gl->GetPotentialInteger(LOCAL_GL_MAX_FRAGMENT_INPUT_COMPONENTS, + &maxFragmentInputComponents)); + + if (ok) { + mGLMaxVaryingVectors = std::min(maxVertexOutputComponents, + maxFragmentInputComponents) / 4; + } else { + mGLMaxVaryingVectors = 16; + // 16 = 64/4, and 64 is the min value for + // maxVertexOutputComponents in the OpenGL 3.2 spec. + } + } + } + + 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); + } + + // Check the shader validator pref + mBypassShaderValidation = gfxPrefs::WebGLBypassShaderValidator(); + + // initialize shader translator + if (!ShInitialize()) { + *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; + } + + mDefaultVertexArray = WebGLVertexArray::Create(this); + mDefaultVertexArray->mAttribs.SetLength(mGLMaxVertexAttribs); + mBoundVertexArray = mDefaultVertexArray; + + // 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. [...]" + + if (gl->IsCoreProfile()) { + mDefaultVertexArray->GenVertexArray(); + mDefaultVertexArray->BindVertexArray(); + } + + mPixelStore_FlipY = false; + mPixelStore_PremultiplyAlpha = false; + mPixelStore_ColorspaceConversion = BROWSER_DEFAULT_WEBGL; + + // GLES 3.0.4, p259: + mPixelStore_UnpackImageHeight = 0; + mPixelStore_UnpackSkipImages = 0; + mPixelStore_UnpackRowLength = 0; + mPixelStore_UnpackSkipRows = 0; + mPixelStore_UnpackSkipPixels = 0; + mPixelStore_UnpackAlignment = 4; + mPixelStore_PackRowLength = 0; + mPixelStore_PackSkipRows = 0; + mPixelStore_PackSkipPixels = 0; + mPixelStore_PackAlignment = 4; + + mPrimRestartTypeBytes = 0; + + mGenericVertexAttribTypes.reset(new GLenum[mGLMaxVertexAttribs]); + std::fill_n(mGenericVertexAttribTypes.get(), mGLMaxVertexAttribs, LOCAL_GL_FLOAT); + + static const float kDefaultGenericVertexAttribData[4] = { 0, 0, 0, 1 }; + memcpy(mGenericVertexAttrib0Data, kDefaultGenericVertexAttribData, + sizeof(mGenericVertexAttrib0Data)); + + mFakeVertexAttrib0BufferObject = 0; + + return true; +} + +bool +WebGLContext::ValidateFramebufferTarget(GLenum target, + const char* const info) +{ + 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(info, "target", target); + return false; +} + +} // namespace mozilla |