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.cpp816
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