michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "WebGLContext.h" michael@0: #include "WebGLBuffer.h" michael@0: #include "WebGLVertexAttribData.h" michael@0: #include "WebGLShader.h" michael@0: #include "WebGLProgram.h" michael@0: #include "WebGLUniformLocation.h" michael@0: #include "WebGLFramebuffer.h" michael@0: #include "WebGLRenderbuffer.h" michael@0: #include "WebGLTexture.h" michael@0: #include "WebGLVertexArray.h" michael@0: #include "GLContext.h" michael@0: #include "CanvasUtils.h" michael@0: michael@0: #include "mozilla/CheckedInt.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/Services.h" michael@0: michael@0: #include "jsfriendapi.h" michael@0: michael@0: #include "angle/ShaderLang.h" michael@0: michael@0: #include michael@0: michael@0: #include "mozilla/Services.h" michael@0: #include "nsIObserverService.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: /** michael@0: * Return the block size for format. michael@0: */ michael@0: static void michael@0: BlockSizeFor(GLenum format, GLint* blockWidth, GLint* blockHeight) michael@0: { michael@0: MOZ_ASSERT(blockWidth && blockHeight); michael@0: michael@0: switch (format) { michael@0: case LOCAL_GL_ATC_RGB: michael@0: case LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA: michael@0: case LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA: michael@0: case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT: michael@0: case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: michael@0: case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: michael@0: case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: michael@0: if (blockWidth) michael@0: *blockWidth = 4; michael@0: if (blockHeight) michael@0: *blockHeight = 4; michael@0: break; michael@0: michael@0: case LOCAL_GL_ETC1_RGB8_OES: michael@0: // 4x4 blocks, but no 4-multiple requirement. michael@0: default: michael@0: break; michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Return the displayable name for the texture function that is the michael@0: * source for validation. michael@0: */ michael@0: static const char* michael@0: InfoFrom(WebGLTexImageFunc func) michael@0: { michael@0: // TODO: Account for dimensions (WebGL 2) michael@0: switch (func) { michael@0: case WebGLTexImageFunc::TexImage: return "texImage2D"; michael@0: case WebGLTexImageFunc::TexSubImage: return "texSubImage2D"; michael@0: case WebGLTexImageFunc::CopyTexImage: return "copyTexImage2D"; michael@0: case WebGLTexImageFunc::CopyTexSubImage: return "copyTexSubImage2D"; michael@0: case WebGLTexImageFunc::CompTexImage: return "compressedTexImage2D"; michael@0: case WebGLTexImageFunc::CompTexSubImage: return "compressedTexSubImage2D"; michael@0: default: michael@0: MOZ_ASSERT(false, "Missing case for WebGLTexImageSource"); michael@0: return "(error)"; michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Return displayable name for GLenum. michael@0: * This version is like gl::GLenumToStr but with out the GL_ prefix to michael@0: * keep consistency with how errors are reported from WebGL. michael@0: */ michael@0: static const char* michael@0: NameFrom(GLenum glenum) michael@0: { michael@0: switch (glenum) { michael@0: #define XX(x) case LOCAL_GL_##x: return #x michael@0: XX(ALPHA); michael@0: XX(ATC_RGB); michael@0: XX(ATC_RGBA_EXPLICIT_ALPHA); michael@0: XX(ATC_RGBA_INTERPOLATED_ALPHA); michael@0: XX(COMPRESSED_RGBA_PVRTC_2BPPV1); michael@0: XX(COMPRESSED_RGBA_PVRTC_4BPPV1); michael@0: XX(COMPRESSED_RGBA_S3TC_DXT1_EXT); michael@0: XX(COMPRESSED_RGBA_S3TC_DXT3_EXT); michael@0: XX(COMPRESSED_RGBA_S3TC_DXT5_EXT); michael@0: XX(COMPRESSED_RGB_PVRTC_2BPPV1); michael@0: XX(COMPRESSED_RGB_PVRTC_4BPPV1); michael@0: XX(COMPRESSED_RGB_S3TC_DXT1_EXT); michael@0: XX(DEPTH_COMPONENT); michael@0: XX(DEPTH_COMPONENT16); michael@0: XX(DEPTH_COMPONENT32); michael@0: XX(DEPTH_STENCIL); michael@0: XX(DEPTH24_STENCIL8); michael@0: XX(ETC1_RGB8_OES); michael@0: XX(FLOAT); michael@0: XX(HALF_FLOAT); michael@0: XX(LUMINANCE); michael@0: XX(LUMINANCE_ALPHA); michael@0: XX(RGB); michael@0: XX(RGB16F); michael@0: XX(RGB32F); michael@0: XX(RGBA); michael@0: XX(RGBA16F); michael@0: XX(RGBA32F); michael@0: XX(SRGB); michael@0: XX(SRGB_ALPHA); michael@0: XX(TEXTURE_2D); michael@0: XX(TEXTURE_3D); michael@0: XX(TEXTURE_CUBE_MAP); michael@0: XX(TEXTURE_CUBE_MAP_NEGATIVE_X); michael@0: XX(TEXTURE_CUBE_MAP_NEGATIVE_Y); michael@0: XX(TEXTURE_CUBE_MAP_NEGATIVE_Z); michael@0: XX(TEXTURE_CUBE_MAP_POSITIVE_X); michael@0: XX(TEXTURE_CUBE_MAP_POSITIVE_Y); michael@0: XX(TEXTURE_CUBE_MAP_POSITIVE_Z); michael@0: XX(UNSIGNED_BYTE); michael@0: XX(UNSIGNED_INT); michael@0: XX(UNSIGNED_INT_24_8); michael@0: XX(UNSIGNED_SHORT); michael@0: XX(UNSIGNED_SHORT_4_4_4_4); michael@0: XX(UNSIGNED_SHORT_5_5_5_1); michael@0: XX(UNSIGNED_SHORT_5_6_5); michael@0: #undef XX michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: /** michael@0: * Same as ErrorInvalidEnum but uses NameFrom to print displayable michael@0: * name for \a glenum. michael@0: */ michael@0: static void michael@0: ErrorInvalidEnumWithName(WebGLContext* ctx, const char* msg, GLenum glenum, WebGLTexImageFunc func) michael@0: { michael@0: const char* name = NameFrom(glenum); michael@0: if (name) michael@0: ctx->ErrorInvalidEnum("%s: %s %s", InfoFrom(func), msg, name); michael@0: else michael@0: ctx->ErrorInvalidEnum("%s: %s 0x%04X", InfoFrom(func), msg, glenum); michael@0: } michael@0: michael@0: /** michael@0: * Same as ErrorInvalidOperation but uses NameFrom to print displayable michael@0: * name for \a glenum. michael@0: */ michael@0: static void michael@0: ErrorInvalidOperationWithName(WebGLContext* ctx, const char* msg, GLenum glenum, michael@0: WebGLTexImageFunc func) michael@0: { michael@0: const char* name = NameFrom(glenum); michael@0: if (name) michael@0: ctx->ErrorInvalidOperation("%s: %s %s", InfoFrom(func), msg, name); michael@0: else michael@0: ctx->ErrorInvalidOperation("%s: %s 0x%04X", InfoFrom(func), msg, glenum); michael@0: } michael@0: michael@0: /** michael@0: * Return true if the format is valid for source calls. michael@0: */ michael@0: static bool michael@0: IsAllowedFromSource(GLenum format, WebGLTexImageFunc func) michael@0: { michael@0: switch (format) { michael@0: case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT: michael@0: case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: michael@0: case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: michael@0: case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: michael@0: case LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1: michael@0: case LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1: michael@0: case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1: michael@0: case LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1: michael@0: return (func == WebGLTexImageFunc::CompTexImage || michael@0: func == WebGLTexImageFunc::CompTexSubImage); michael@0: michael@0: case LOCAL_GL_ATC_RGB: michael@0: case LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA: michael@0: case LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA: michael@0: case LOCAL_GL_ETC1_RGB8_OES: michael@0: return func == WebGLTexImageFunc::CompTexImage; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: /** michael@0: * Returns true if func is a CopyTexImage variant. michael@0: */ michael@0: static bool michael@0: IsCopyFunc(WebGLTexImageFunc func) michael@0: { michael@0: return (func == WebGLTexImageFunc::CopyTexImage || michael@0: func == WebGLTexImageFunc::CopyTexSubImage); michael@0: } michael@0: michael@0: /** michael@0: * Returns true if func is a SubImage variant. michael@0: */ michael@0: static bool michael@0: IsSubFunc(WebGLTexImageFunc func) michael@0: { michael@0: return (func == WebGLTexImageFunc::TexSubImage || michael@0: func == WebGLTexImageFunc::CopyTexSubImage || michael@0: func == WebGLTexImageFunc::CompTexSubImage); michael@0: } michael@0: michael@0: /** michael@0: * returns true is target is a texture cube map target. michael@0: */ michael@0: static bool michael@0: IsTexImageCubemapTarget(GLenum target) michael@0: { michael@0: return (target >= LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X && michael@0: target <= LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z); michael@0: } michael@0: michael@0: /* michael@0: * Pull data out of the program, post-linking michael@0: */ michael@0: bool michael@0: WebGLProgram::UpdateInfo() michael@0: { michael@0: mIdentifierMap = nullptr; michael@0: mIdentifierReverseMap = nullptr; michael@0: mUniformInfoMap = nullptr; michael@0: michael@0: mAttribMaxNameLength = 0; michael@0: michael@0: for (size_t i = 0; i < mAttachedShaders.Length(); i++) michael@0: mAttribMaxNameLength = std::max(mAttribMaxNameLength, mAttachedShaders[i]->mAttribMaxNameLength); michael@0: michael@0: GLint attribCount; michael@0: mContext->gl->fGetProgramiv(mGLName, LOCAL_GL_ACTIVE_ATTRIBUTES, &attribCount); michael@0: michael@0: if (!mAttribsInUse.SetLength(mContext->mGLMaxVertexAttribs)) { michael@0: mContext->ErrorOutOfMemory("updateInfo: out of memory to allocate %d attribs", mContext->mGLMaxVertexAttribs); michael@0: return false; michael@0: } michael@0: michael@0: for (size_t i = 0; i < mAttribsInUse.Length(); i++) michael@0: mAttribsInUse[i] = false; michael@0: michael@0: nsAutoArrayPtr nameBuf(new char[mAttribMaxNameLength]); michael@0: michael@0: for (int i = 0; i < attribCount; ++i) { michael@0: GLint attrnamelen; michael@0: GLint attrsize; michael@0: GLenum attrtype; michael@0: mContext->gl->fGetActiveAttrib(mGLName, i, mAttribMaxNameLength, &attrnamelen, &attrsize, &attrtype, nameBuf); michael@0: if (attrnamelen > 0) { michael@0: GLint loc = mContext->gl->fGetAttribLocation(mGLName, nameBuf); michael@0: MOZ_ASSERT(loc >= 0, "major oops in managing the attributes of a WebGL program"); michael@0: if (loc < mContext->mGLMaxVertexAttribs) { michael@0: mAttribsInUse[loc] = true; michael@0: } else { michael@0: mContext->GenerateWarning("program exceeds MAX_VERTEX_ATTRIBS"); michael@0: return false; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (!mUniformInfoMap) { michael@0: mUniformInfoMap = new CStringToUniformInfoMap; michael@0: for (size_t i = 0; i < mAttachedShaders.Length(); i++) { michael@0: for (size_t j = 0; j < mAttachedShaders[i]->mUniforms.Length(); j++) { michael@0: const WebGLMappedIdentifier& uniform = mAttachedShaders[i]->mUniforms[j]; michael@0: const WebGLUniformInfo& info = mAttachedShaders[i]->mUniformInfos[j]; michael@0: mUniformInfoMap->Put(uniform.mapped, info); michael@0: } michael@0: } michael@0: } michael@0: michael@0: mActiveAttribMap.clear(); michael@0: michael@0: GLint numActiveAttrs = 0; michael@0: mContext->gl->fGetProgramiv(mGLName, LOCAL_GL_ACTIVE_ATTRIBUTES, &numActiveAttrs); michael@0: michael@0: // Spec says the maximum attrib name length is 256 chars, so this is michael@0: // sufficient to hold any attrib name. michael@0: char attrName[257]; michael@0: michael@0: GLint dummySize; michael@0: GLenum dummyType; michael@0: for (GLint i = 0; i < numActiveAttrs; i++) { michael@0: mContext->gl->fGetActiveAttrib(mGLName, i, 257, nullptr, &dummySize, michael@0: &dummyType, attrName); michael@0: GLint attrLoc = mContext->gl->fGetAttribLocation(mGLName, attrName); michael@0: MOZ_ASSERT(attrLoc >= 0); michael@0: mActiveAttribMap.insert(std::make_pair(attrLoc, nsCString(attrName))); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: /** michael@0: * Return the simple base format for a given internal format. michael@0: * michael@0: * \return the corresponding \u base internal format (GL_ALPHA, GL_LUMINANCE, michael@0: * GL_LUMINANCE_ALPHA, GL_RGB, GL_RGBA), or GL_NONE if invalid enum. michael@0: */ michael@0: GLenum michael@0: WebGLContext::BaseTexFormat(GLenum internalFormat) const michael@0: { michael@0: if (internalFormat == LOCAL_GL_ALPHA || michael@0: internalFormat == LOCAL_GL_LUMINANCE || michael@0: internalFormat == LOCAL_GL_LUMINANCE_ALPHA || michael@0: internalFormat == LOCAL_GL_RGB || michael@0: internalFormat == LOCAL_GL_RGBA) michael@0: { michael@0: return internalFormat; michael@0: } michael@0: michael@0: if (IsExtensionEnabled(WebGLExtensionID::EXT_sRGB)) { michael@0: if (internalFormat == LOCAL_GL_SRGB) michael@0: return LOCAL_GL_RGB; michael@0: michael@0: if (internalFormat == LOCAL_GL_SRGB_ALPHA) michael@0: return LOCAL_GL_RGBA; michael@0: } michael@0: michael@0: if (IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_atc)) { michael@0: if (internalFormat == LOCAL_GL_ATC_RGB) michael@0: return LOCAL_GL_RGB; michael@0: michael@0: if (internalFormat == LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA || michael@0: internalFormat == LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA) michael@0: { michael@0: return LOCAL_GL_RGBA; michael@0: } michael@0: } michael@0: michael@0: if (IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_etc1)) { michael@0: if (internalFormat == LOCAL_GL_ETC1_RGB8_OES) michael@0: return LOCAL_GL_RGB; michael@0: } michael@0: michael@0: if (IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_pvrtc)) { michael@0: if (internalFormat == LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1 || michael@0: internalFormat == LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1) michael@0: { michael@0: return LOCAL_GL_RGB; michael@0: } michael@0: michael@0: if (internalFormat == LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1 || michael@0: internalFormat == LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1) michael@0: { michael@0: return LOCAL_GL_RGBA; michael@0: } michael@0: } michael@0: michael@0: if (IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_s3tc)) { michael@0: if (internalFormat == LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT) michael@0: return LOCAL_GL_RGB; michael@0: michael@0: if (internalFormat == LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT || michael@0: internalFormat == LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT || michael@0: internalFormat == LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT) michael@0: { michael@0: return LOCAL_GL_RGBA; michael@0: } michael@0: } michael@0: michael@0: if (IsExtensionEnabled(WebGLExtensionID::WEBGL_depth_texture)) { michael@0: if (internalFormat == LOCAL_GL_DEPTH_COMPONENT || michael@0: internalFormat == LOCAL_GL_DEPTH_COMPONENT16 || michael@0: internalFormat == LOCAL_GL_DEPTH_COMPONENT32) michael@0: { michael@0: return LOCAL_GL_DEPTH_COMPONENT; michael@0: } michael@0: michael@0: if (internalFormat == LOCAL_GL_DEPTH_STENCIL || michael@0: internalFormat == LOCAL_GL_DEPTH24_STENCIL8) michael@0: { michael@0: return LOCAL_GL_DEPTH_STENCIL; michael@0: } michael@0: } michael@0: michael@0: MOZ_ASSERT(false, "Unhandled internalFormat"); michael@0: return LOCAL_GL_NONE; michael@0: } michael@0: michael@0: bool WebGLContext::ValidateBlendEquationEnum(GLenum mode, const char *info) michael@0: { michael@0: switch (mode) { michael@0: case LOCAL_GL_FUNC_ADD: michael@0: case LOCAL_GL_FUNC_SUBTRACT: michael@0: case LOCAL_GL_FUNC_REVERSE_SUBTRACT: michael@0: return true; michael@0: case LOCAL_GL_MIN: michael@0: case LOCAL_GL_MAX: michael@0: if (IsWebGL2()) { michael@0: // http://www.opengl.org/registry/specs/EXT/blend_minmax.txt michael@0: return true; michael@0: } michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: michael@0: ErrorInvalidEnumInfo(info, mode); michael@0: return false; michael@0: } michael@0: michael@0: bool WebGLContext::ValidateBlendFuncDstEnum(GLenum factor, const char *info) michael@0: { michael@0: switch (factor) { michael@0: case LOCAL_GL_ZERO: michael@0: case LOCAL_GL_ONE: michael@0: case LOCAL_GL_SRC_COLOR: michael@0: case LOCAL_GL_ONE_MINUS_SRC_COLOR: michael@0: case LOCAL_GL_DST_COLOR: michael@0: case LOCAL_GL_ONE_MINUS_DST_COLOR: michael@0: case LOCAL_GL_SRC_ALPHA: michael@0: case LOCAL_GL_ONE_MINUS_SRC_ALPHA: michael@0: case LOCAL_GL_DST_ALPHA: michael@0: case LOCAL_GL_ONE_MINUS_DST_ALPHA: michael@0: case LOCAL_GL_CONSTANT_COLOR: michael@0: case LOCAL_GL_ONE_MINUS_CONSTANT_COLOR: michael@0: case LOCAL_GL_CONSTANT_ALPHA: michael@0: case LOCAL_GL_ONE_MINUS_CONSTANT_ALPHA: michael@0: return true; michael@0: default: michael@0: ErrorInvalidEnumInfo(info, factor); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: bool WebGLContext::ValidateBlendFuncSrcEnum(GLenum factor, const char *info) michael@0: { michael@0: if (factor == LOCAL_GL_SRC_ALPHA_SATURATE) michael@0: return true; michael@0: else michael@0: return ValidateBlendFuncDstEnum(factor, info); michael@0: } michael@0: michael@0: bool WebGLContext::ValidateBlendFuncEnumsCompatibility(GLenum sfactor, GLenum dfactor, const char *info) michael@0: { michael@0: bool sfactorIsConstantColor = sfactor == LOCAL_GL_CONSTANT_COLOR || michael@0: sfactor == LOCAL_GL_ONE_MINUS_CONSTANT_COLOR; michael@0: bool sfactorIsConstantAlpha = sfactor == LOCAL_GL_CONSTANT_ALPHA || michael@0: sfactor == LOCAL_GL_ONE_MINUS_CONSTANT_ALPHA; michael@0: bool dfactorIsConstantColor = dfactor == LOCAL_GL_CONSTANT_COLOR || michael@0: dfactor == LOCAL_GL_ONE_MINUS_CONSTANT_COLOR; michael@0: bool dfactorIsConstantAlpha = dfactor == LOCAL_GL_CONSTANT_ALPHA || michael@0: dfactor == LOCAL_GL_ONE_MINUS_CONSTANT_ALPHA; michael@0: if ( (sfactorIsConstantColor && dfactorIsConstantAlpha) || michael@0: (dfactorIsConstantColor && sfactorIsConstantAlpha) ) { michael@0: ErrorInvalidOperation("%s are mutually incompatible, see section 6.8 in the WebGL 1.0 spec", info); michael@0: return false; michael@0: } else { michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: bool WebGLContext::ValidateTextureTargetEnum(GLenum target, const char *info) michael@0: { michael@0: switch (target) { michael@0: case LOCAL_GL_TEXTURE_2D: michael@0: case LOCAL_GL_TEXTURE_CUBE_MAP: michael@0: return true; michael@0: default: michael@0: ErrorInvalidEnumInfo(info, target); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: bool WebGLContext::ValidateComparisonEnum(GLenum target, const char *info) michael@0: { michael@0: switch (target) { michael@0: case LOCAL_GL_NEVER: michael@0: case LOCAL_GL_LESS: michael@0: case LOCAL_GL_LEQUAL: michael@0: case LOCAL_GL_GREATER: michael@0: case LOCAL_GL_GEQUAL: michael@0: case LOCAL_GL_EQUAL: michael@0: case LOCAL_GL_NOTEQUAL: michael@0: case LOCAL_GL_ALWAYS: michael@0: return true; michael@0: default: michael@0: ErrorInvalidEnumInfo(info, target); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: bool WebGLContext::ValidateStencilOpEnum(GLenum action, const char *info) michael@0: { michael@0: switch (action) { michael@0: case LOCAL_GL_KEEP: michael@0: case LOCAL_GL_ZERO: michael@0: case LOCAL_GL_REPLACE: michael@0: case LOCAL_GL_INCR: michael@0: case LOCAL_GL_INCR_WRAP: michael@0: case LOCAL_GL_DECR: michael@0: case LOCAL_GL_DECR_WRAP: michael@0: case LOCAL_GL_INVERT: michael@0: return true; michael@0: default: michael@0: ErrorInvalidEnumInfo(info, action); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: bool WebGLContext::ValidateFaceEnum(GLenum face, const char *info) michael@0: { michael@0: switch (face) { michael@0: case LOCAL_GL_FRONT: michael@0: case LOCAL_GL_BACK: michael@0: case LOCAL_GL_FRONT_AND_BACK: michael@0: return true; michael@0: default: michael@0: ErrorInvalidEnumInfo(info, face); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: bool WebGLContext::ValidateDrawModeEnum(GLenum mode, const char *info) michael@0: { michael@0: switch (mode) { michael@0: case LOCAL_GL_TRIANGLES: michael@0: case LOCAL_GL_TRIANGLE_STRIP: michael@0: case LOCAL_GL_TRIANGLE_FAN: michael@0: case LOCAL_GL_POINTS: michael@0: case LOCAL_GL_LINE_STRIP: michael@0: case LOCAL_GL_LINE_LOOP: michael@0: case LOCAL_GL_LINES: michael@0: return true; michael@0: default: michael@0: ErrorInvalidEnumInfo(info, mode); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: bool WebGLContext::ValidateGLSLVariableName(const nsAString& name, const char *info) michael@0: { michael@0: if (name.IsEmpty()) michael@0: return false; michael@0: michael@0: const uint32_t maxSize = 256; michael@0: if (name.Length() > maxSize) { michael@0: ErrorInvalidValue("%s: identifier is %d characters long, exceeds the maximum allowed length of %d characters", michael@0: info, name.Length(), maxSize); michael@0: return false; michael@0: } michael@0: michael@0: if (!ValidateGLSLString(name, info)) { michael@0: return false; michael@0: } michael@0: michael@0: nsString prefix1 = NS_LITERAL_STRING("webgl_"); michael@0: nsString prefix2 = NS_LITERAL_STRING("_webgl_"); michael@0: michael@0: if (Substring(name, 0, prefix1.Length()).Equals(prefix1) || michael@0: Substring(name, 0, prefix2.Length()).Equals(prefix2)) michael@0: { michael@0: ErrorInvalidOperation("%s: string contains a reserved GLSL prefix", info); michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool WebGLContext::ValidateGLSLString(const nsAString& string, const char *info) michael@0: { michael@0: for (uint32_t i = 0; i < string.Length(); ++i) { michael@0: if (!ValidateGLSLCharacter(string.CharAt(i))) { michael@0: ErrorInvalidValue("%s: string contains the illegal character '%d'", info, string.CharAt(i)); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: /** michael@0: * Return true if format is a valid texture image format for source, michael@0: * taking into account enabled WebGL extensions. michael@0: */ michael@0: bool michael@0: WebGLContext::ValidateTexImageFormat(GLenum format, WebGLTexImageFunc func) michael@0: { michael@0: /* Core WebGL texture formats */ michael@0: if (format == LOCAL_GL_ALPHA || michael@0: format == LOCAL_GL_RGB || michael@0: format == LOCAL_GL_RGBA || michael@0: format == LOCAL_GL_LUMINANCE || michael@0: format == LOCAL_GL_LUMINANCE_ALPHA) michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: /* Only core formats are valid for CopyTex(Sub)?Image */ michael@0: // TODO: Revisit this once color_buffer_(half_)?float lands michael@0: if (IsCopyFunc(func)) { michael@0: ErrorInvalidEnumWithName(this, "invalid format", format, func); michael@0: return false; michael@0: } michael@0: michael@0: /* WEBGL_depth_texture added formats */ michael@0: if (format == LOCAL_GL_DEPTH_COMPONENT || michael@0: format == LOCAL_GL_DEPTH_STENCIL) michael@0: { michael@0: bool validFormat = IsExtensionEnabled(WebGLExtensionID::WEBGL_depth_texture); michael@0: if (!validFormat) michael@0: ErrorInvalidEnum("%s: invalid format %s: need WEBGL_depth_texture enabled", michael@0: InfoFrom(func), NameFrom(format)); michael@0: return validFormat; michael@0: } michael@0: michael@0: /* EXT_sRGB added formats */ michael@0: if (format == LOCAL_GL_SRGB || michael@0: format == LOCAL_GL_SRGB_ALPHA) michael@0: { michael@0: bool validFormat = IsExtensionEnabled(WebGLExtensionID::EXT_sRGB); michael@0: if (!validFormat) michael@0: ErrorInvalidEnum("%s: invalid format %s: need EXT_sRGB enabled", michael@0: InfoFrom(func), NameFrom(format)); michael@0: return validFormat; michael@0: } michael@0: michael@0: /* WEBGL_compressed_texture_atc added formats */ michael@0: if (format == LOCAL_GL_ATC_RGB || michael@0: format == LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA || michael@0: format == LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA) michael@0: { michael@0: bool validFormat = IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_atc); michael@0: if (!validFormat) michael@0: ErrorInvalidEnum("%s: invalid format %s: need WEBGL_compressed_texture_atc enabled", michael@0: InfoFrom(func), NameFrom(format)); michael@0: return validFormat; michael@0: } michael@0: michael@0: // WEBGL_compressed_texture_etc1 michael@0: if (format == LOCAL_GL_ETC1_RGB8_OES) { michael@0: bool validFormat = IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_etc1); michael@0: if (!validFormat) michael@0: ErrorInvalidEnum("%s: invalid format %s: need WEBGL_compressed_texture_etc1 enabled", michael@0: InfoFrom(func), NameFrom(format)); michael@0: return validFormat; michael@0: } michael@0: michael@0: michael@0: if (format == LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1 || michael@0: format == LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1 || michael@0: format == LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1 || michael@0: format == LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1) michael@0: { michael@0: bool validFormat = IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_pvrtc); michael@0: if (!validFormat) michael@0: ErrorInvalidEnum("%s: invalid format %s: need WEBGL_compressed_texture_pvrtc enabled", michael@0: InfoFrom(func), NameFrom(format)); michael@0: return validFormat; michael@0: } michael@0: michael@0: michael@0: if (format == LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT || michael@0: format == LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT || michael@0: format == LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT || michael@0: format == LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT) michael@0: { michael@0: bool validFormat = IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_s3tc); michael@0: if (!validFormat) michael@0: ErrorInvalidEnum("%s: invalid format %s: need WEBGL_compressed_texture_s3tc enabled", michael@0: InfoFrom(func), NameFrom(format)); michael@0: return validFormat; michael@0: } michael@0: michael@0: ErrorInvalidEnumWithName(this, "invalid format", format, func); michael@0: michael@0: return false; michael@0: } michael@0: michael@0: /** michael@0: * Check if the given texture target is valid for TexImage. michael@0: */ michael@0: bool michael@0: WebGLContext::ValidateTexImageTarget(GLuint dims, GLenum target, WebGLTexImageFunc func) michael@0: { michael@0: switch (dims) { michael@0: case 2: michael@0: if (target == LOCAL_GL_TEXTURE_2D || michael@0: IsTexImageCubemapTarget(target)) michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: ErrorInvalidEnumWithName(this, "invalid target", target, func); michael@0: return false; michael@0: michael@0: default: michael@0: MOZ_ASSERT(false, "ValidateTexImageTarget: Invalid dims"); michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: /** michael@0: * Return true if type is a valid texture image type for source, michael@0: * taking into account enabled WebGL extensions. michael@0: */ michael@0: bool michael@0: WebGLContext::ValidateTexImageType(GLenum type, WebGLTexImageFunc func) michael@0: { michael@0: /* Core WebGL texture types */ michael@0: if (type == LOCAL_GL_UNSIGNED_BYTE || michael@0: type == LOCAL_GL_UNSIGNED_SHORT_5_6_5 || michael@0: type == LOCAL_GL_UNSIGNED_SHORT_4_4_4_4 || michael@0: type == LOCAL_GL_UNSIGNED_SHORT_5_5_5_1) michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: /* OES_texture_float added types */ michael@0: if (type == LOCAL_GL_FLOAT) { michael@0: bool validType = IsExtensionEnabled(WebGLExtensionID::OES_texture_float); michael@0: if (!validType) michael@0: ErrorInvalidEnum("%s: invalid type %s: need OES_texture_float enabled", michael@0: InfoFrom(func), NameFrom(type)); michael@0: return validType; michael@0: } michael@0: michael@0: /* OES_texture_half_float add types */ michael@0: if (type == LOCAL_GL_HALF_FLOAT_OES) { michael@0: bool validType = IsExtensionEnabled(WebGLExtensionID::OES_texture_half_float); michael@0: if (!validType) michael@0: ErrorInvalidEnum("%s: invalid type %s: need OES_texture_half_float enabled", michael@0: InfoFrom(func), NameFrom(type)); michael@0: return validType; michael@0: } michael@0: michael@0: /* WEBGL_depth_texture added types */ michael@0: if (type == LOCAL_GL_UNSIGNED_SHORT || michael@0: type == LOCAL_GL_UNSIGNED_INT || michael@0: type == LOCAL_GL_UNSIGNED_INT_24_8) michael@0: { michael@0: bool validType = IsExtensionEnabled(WebGLExtensionID::WEBGL_depth_texture); michael@0: if (!validType) michael@0: ErrorInvalidEnum("%s: invalid type %s: need WEBGL_depth_texture enabled", michael@0: InfoFrom(func), NameFrom(type)); michael@0: return validType; michael@0: } michael@0: michael@0: ErrorInvalidEnumWithName(this, "invalid type", type, func); michael@0: return false; michael@0: } michael@0: michael@0: /** michael@0: * Validate texture image sizing extra constraints for michael@0: * CompressedTex(Sub)?Image. michael@0: */ michael@0: // TODO: WebGL 2 michael@0: bool michael@0: WebGLContext::ValidateCompTexImageSize(GLenum target, GLint level, GLenum format, michael@0: GLint xoffset, GLint yoffset, michael@0: GLsizei width, GLsizei height, michael@0: GLsizei levelWidth, GLsizei levelHeight, michael@0: WebGLTexImageFunc func) michael@0: { michael@0: // Negative parameters must already have been handled above michael@0: MOZ_ASSERT(xoffset >= 0 && yoffset >= 0 && michael@0: width >= 0 && height >= 0); michael@0: michael@0: if (xoffset + width > (GLint) levelWidth) { michael@0: ErrorInvalidValue("%s: xoffset + width must be <= levelWidth", InfoFrom(func)); michael@0: return false; michael@0: } michael@0: michael@0: if (yoffset + height > (GLint) levelHeight) { michael@0: ErrorInvalidValue("%s: yoffset + height must be <= levelHeight", InfoFrom(func)); michael@0: return false; michael@0: } michael@0: michael@0: GLint blockWidth = 1; michael@0: GLint blockHeight = 1; michael@0: BlockSizeFor(format, &blockWidth, &blockHeight); michael@0: michael@0: /* If blockWidth || blockHeight != 1, then the compressed format michael@0: * had block-based constraints to be checked. (For example, PVRTC is compressed but michael@0: * isn't a block-based format) michael@0: */ michael@0: if (blockWidth != 1 || blockHeight != 1) { michael@0: /* offsets must be multiple of block size */ michael@0: if (xoffset % blockWidth != 0) { michael@0: ErrorInvalidOperation("%s: xoffset must be multiple of %d", michael@0: InfoFrom(func), blockWidth); michael@0: return false; michael@0: } michael@0: michael@0: if (yoffset % blockHeight != 0) { michael@0: ErrorInvalidOperation("%s: yoffset must be multiple of %d", michael@0: InfoFrom(func), blockHeight); michael@0: return false; michael@0: } michael@0: michael@0: /* The size must be a multiple of blockWidth and blockHeight, michael@0: * or must be using offset+size that exactly hits the edge. michael@0: * Important for small mipmap levels. michael@0: */ michael@0: /* https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/ michael@0: * "When level equals zero width and height must be a multiple of 4. When michael@0: * level is greater than 0 width and height must be 0, 1, 2 or a multiple of 4. michael@0: * If they are not an INVALID_OPERATION error is generated." michael@0: */ michael@0: if (level == 0) { michael@0: if (width % blockWidth != 0) { michael@0: ErrorInvalidOperation("%s: width of level 0 must be multple of %d", michael@0: InfoFrom(func), blockWidth); michael@0: return false; michael@0: } michael@0: michael@0: if (height % blockHeight != 0) { michael@0: ErrorInvalidOperation("%s: height of level 0 must be multipel of %d", michael@0: InfoFrom(func), blockHeight); michael@0: return false; michael@0: } michael@0: } michael@0: else if (level > 0) { michael@0: if (width % blockWidth != 0 && width > 2) { michael@0: ErrorInvalidOperation("%s: width of level %d must be multiple" michael@0: " of %d or 0, 1, 2", michael@0: InfoFrom(func), level, blockWidth); michael@0: return false; michael@0: } michael@0: michael@0: if (height % blockHeight != 0 && height > 2) { michael@0: ErrorInvalidOperation("%s: height of level %d must be multiple" michael@0: " of %d or 0, 1, 2", michael@0: InfoFrom(func), level, blockHeight); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: if (IsSubFunc(func)) { michael@0: if ((xoffset % blockWidth) != 0) { michael@0: ErrorInvalidOperation("%s: xoffset must be multiple of %d", michael@0: InfoFrom(func), blockWidth); michael@0: return false; michael@0: } michael@0: michael@0: if (yoffset % blockHeight != 0) { michael@0: ErrorInvalidOperation("%s: yoffset must be multiple of %d", michael@0: InfoFrom(func), blockHeight); michael@0: return false; michael@0: } michael@0: } michael@0: } michael@0: michael@0: switch (format) { michael@0: case LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1: michael@0: case LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1: michael@0: case LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1: michael@0: case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1: michael@0: if (!is_pot_assuming_nonnegative(width) || michael@0: !is_pot_assuming_nonnegative(height)) michael@0: { michael@0: ErrorInvalidValue("%s: width and height must be powers of two", michael@0: InfoFrom(func)); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: /** michael@0: * Return true if the enough data is present to satisfy compressed michael@0: * texture format constraints. michael@0: */ michael@0: bool michael@0: WebGLContext::ValidateCompTexImageDataSize(GLint level, GLenum format, michael@0: GLsizei width, GLsizei height, michael@0: uint32_t byteLength, WebGLTexImageFunc func) michael@0: { michael@0: // negative width and height must already have been handled above michael@0: MOZ_ASSERT(width >= 0 && height >= 0); michael@0: michael@0: CheckedUint32 required_byteLength = 0; michael@0: michael@0: switch (format) { michael@0: case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT: michael@0: case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: michael@0: case LOCAL_GL_ATC_RGB: michael@0: case LOCAL_GL_ETC1_RGB8_OES: michael@0: { michael@0: required_byteLength = ((CheckedUint32(width) + 3) / 4) * ((CheckedUint32(height) + 3) / 4) * 8; michael@0: break; michael@0: } michael@0: case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: michael@0: case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: michael@0: case LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA: michael@0: case LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA: michael@0: { michael@0: required_byteLength = ((CheckedUint32(width) + 3) / 4) * ((CheckedUint32(height) + 3) / 4) * 16; michael@0: break; michael@0: } michael@0: case LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1: michael@0: case LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1: michael@0: { michael@0: required_byteLength = CheckedUint32(std::max(width, 8)) * CheckedUint32(std::max(height, 8)) / 2; michael@0: break; michael@0: } michael@0: case LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1: michael@0: case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1: michael@0: { michael@0: required_byteLength = CheckedUint32(std::max(width, 16)) * CheckedUint32(std::max(height, 8)) / 4; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (!required_byteLength.isValid() || required_byteLength.value() != byteLength) { michael@0: ErrorInvalidValue("%s: data size does not match dimensions", InfoFrom(func)); michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: /** michael@0: * Validate the width, height, and depth of a texture image, \return michael@0: * true is valid, false otherwise. michael@0: * Used by all the (Compressed|Copy)?Tex(Sub)?Image functions. michael@0: * Target and level must have been validated before calling. michael@0: */ michael@0: bool michael@0: WebGLContext::ValidateTexImageSize(GLenum target, GLint level, michael@0: GLint width, GLint height, GLint depth, michael@0: WebGLTexImageFunc func) michael@0: { michael@0: MOZ_ASSERT(level >= 0, "level should already be validated"); michael@0: michael@0: /* Bug 966630: maxTextureSize >> level runs into "undefined" michael@0: * behaviour depending on ISA. For example, on Intel shifts michael@0: * amounts are mod 64 (in 64-bit mode on 64-bit dest) and mod 32 michael@0: * otherwise. This means 16384 >> 0x10000001 == 8192 which isn't michael@0: * what would be expected. Make the required behaviour explicit by michael@0: * clamping to a shift of 31 bits if level is greater than that michael@0: * ammount. This will give 0 that if (!maxAllowedSize) is michael@0: * expecting. michael@0: */ michael@0: michael@0: if (level > 31) michael@0: level = 31; michael@0: michael@0: const GLuint maxTexImageSize = MaxTextureSizeForTarget(target) >> level; michael@0: const bool isCubemapTarget = IsTexImageCubemapTarget(target); michael@0: const bool isSub = IsSubFunc(func); michael@0: michael@0: if (!isSub && isCubemapTarget && (width != height)) { michael@0: /* GL ES Version 2.0.25 - 3.7.1 Texture Image Specification michael@0: * "When the target parameter to TexImage2D is one of the michael@0: * six cube map two-dimensional image targets, the error michael@0: * INVALID_VALUE is generated if the width and height michael@0: * parameters are not equal." michael@0: */ michael@0: ErrorInvalidValue("%s: for cube map, width must equal height", InfoFrom(func)); michael@0: return false; michael@0: } michael@0: michael@0: if (target == LOCAL_GL_TEXTURE_2D || isCubemapTarget) michael@0: { michael@0: /* GL ES Version 2.0.25 - 3.7.1 Texture Image Specification michael@0: * "If wt and ht are the specified image width and height, michael@0: * and if either wt or ht are less than zero, then the error michael@0: * INVALID_VALUE is generated." michael@0: */ michael@0: if (width < 0) { michael@0: ErrorInvalidValue("%s: width must be >= 0", InfoFrom(func)); michael@0: return false; michael@0: } michael@0: michael@0: if (height < 0) { michael@0: ErrorInvalidValue("%s: height must be >= 0", InfoFrom(func)); michael@0: return false; michael@0: } michael@0: michael@0: /* GL ES Version 2.0.25 - 3.7.1 Texture Image Specification michael@0: * "The maximum allowable width and height of a michael@0: * two-dimensional texture image must be at least 2**(k−lod) michael@0: * for image arrays of level zero through k, where k is the michael@0: * log base 2 of MAX_TEXTURE_SIZE. and lod is the michael@0: * level-of-detail of the image array. It may be zero for michael@0: * image arrays of any level-of-detail greater than k. The michael@0: * error INVALID_VALUE is generated if the specified image michael@0: * is too large to be stored under any conditions. michael@0: */ michael@0: if (width > (int) maxTexImageSize) { michael@0: ErrorInvalidValue("%s: the maximum width for level %d is %u", michael@0: InfoFrom(func), level, maxTexImageSize); michael@0: return false; michael@0: } michael@0: michael@0: if (height > (int) maxTexImageSize) { michael@0: ErrorInvalidValue("%s: tex maximum height for level %d is %u", michael@0: InfoFrom(func), level, maxTexImageSize); michael@0: return false; michael@0: } michael@0: michael@0: /* GL ES Version 2.0.25 - 3.7.1 Texture Image Specification michael@0: * "If level is greater than zero, and either width or michael@0: * height is not a power-of-two, the error INVALID_VALUE is michael@0: * generated." michael@0: */ michael@0: if (level > 0) { michael@0: if (!is_pot_assuming_nonnegative(width)) { michael@0: ErrorInvalidValue("%s: level >= 0, width of %d must be a power of two.", michael@0: InfoFrom(func), width); michael@0: return false; michael@0: } michael@0: michael@0: if (!is_pot_assuming_nonnegative(height)) { michael@0: ErrorInvalidValue("%s: level >= 0, height of %d must be a power of two.", michael@0: InfoFrom(func), height); michael@0: return false; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // TODO: WebGL 2 michael@0: if (target == LOCAL_GL_TEXTURE_3D) { michael@0: if (depth < 0) { michael@0: ErrorInvalidValue("%s: depth must be >= 0", InfoFrom(func)); michael@0: return false; michael@0: } michael@0: michael@0: if (!is_pot_assuming_nonnegative(depth)) { michael@0: ErrorInvalidValue("%s: level >= 0, depth of %d must be a power of two.", michael@0: InfoFrom(func), depth); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: /** michael@0: * Validate texture image sizing for Tex(Sub)?Image variants. michael@0: */ michael@0: // TODO: WebGL 2. Update this to handle 3D textures. michael@0: bool michael@0: WebGLContext::ValidateTexSubImageSize(GLint xoffset, GLint yoffset, GLint /*zoffset*/, michael@0: GLsizei width, GLsizei height, GLsizei /*depth*/, michael@0: GLsizei baseWidth, GLsizei baseHeight, GLsizei /*baseDepth*/, michael@0: WebGLTexImageFunc func) michael@0: { michael@0: /* GL ES Version 2.0.25 - 3.7.1 Texture Image Specification michael@0: * "Taking wt and ht to be the specified width and height of the michael@0: * texture array, and taking x, y, w, and h to be the xoffset, michael@0: * yoffset, width, and height argument values, any of the michael@0: * following relationships generates the error INVALID_VALUE: michael@0: * x < 0 michael@0: * x + w > wt michael@0: * y < 0 michael@0: * y + h > ht" michael@0: */ michael@0: michael@0: if (xoffset < 0) { michael@0: ErrorInvalidValue("%s: xoffset must be >= 0", InfoFrom(func)); michael@0: return false; michael@0: } michael@0: michael@0: if (yoffset < 0) { michael@0: ErrorInvalidValue("%s: yoffset must be >= 0", InfoFrom(func)); michael@0: return false; michael@0: } michael@0: michael@0: if (!CanvasUtils::CheckSaneSubrectSize(xoffset, yoffset, width, height, baseWidth, baseHeight)) { michael@0: ErrorInvalidValue("%s: subtexture rectangle out-of-bounds", InfoFrom(func)); michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: /** michael@0: * Return the bits per texel for format & type combination. michael@0: * Assumes that format & type are a valid combination as checked with michael@0: * ValidateTexImageFormatAndType(). michael@0: */ michael@0: uint32_t michael@0: WebGLContext::GetBitsPerTexel(GLenum format, GLenum type) michael@0: { michael@0: // If there is no defined format or type, we're not taking up any memory michael@0: if (!format || !type) { michael@0: return 0; michael@0: } michael@0: michael@0: /* Known fixed-sized types */ michael@0: if (type == LOCAL_GL_UNSIGNED_SHORT_4_4_4_4 || michael@0: type == LOCAL_GL_UNSIGNED_SHORT_5_5_5_1 || michael@0: type == LOCAL_GL_UNSIGNED_SHORT_5_6_5) michael@0: { michael@0: return 16; michael@0: } michael@0: michael@0: if (type == LOCAL_GL_UNSIGNED_INT_24_8) michael@0: return 32; michael@0: michael@0: int bitsPerComponent = 0; michael@0: switch (type) { michael@0: case LOCAL_GL_UNSIGNED_BYTE: michael@0: bitsPerComponent = 8; michael@0: break; michael@0: michael@0: case LOCAL_GL_HALF_FLOAT: michael@0: case LOCAL_GL_HALF_FLOAT_OES: michael@0: case LOCAL_GL_UNSIGNED_SHORT: michael@0: bitsPerComponent = 16; michael@0: break; michael@0: michael@0: case LOCAL_GL_FLOAT: michael@0: case LOCAL_GL_UNSIGNED_INT: michael@0: bitsPerComponent = 32; michael@0: break; michael@0: michael@0: default: michael@0: MOZ_ASSERT(false, "Unhandled type."); michael@0: break; michael@0: } michael@0: michael@0: switch (format) { michael@0: // Uncompressed formats michael@0: case LOCAL_GL_ALPHA: michael@0: case LOCAL_GL_LUMINANCE: michael@0: case LOCAL_GL_DEPTH_COMPONENT: michael@0: case LOCAL_GL_DEPTH_STENCIL: michael@0: return 1 * bitsPerComponent; michael@0: michael@0: case LOCAL_GL_LUMINANCE_ALPHA: michael@0: return 2 * bitsPerComponent; michael@0: michael@0: case LOCAL_GL_RGB: michael@0: case LOCAL_GL_RGB32F: michael@0: case LOCAL_GL_SRGB_EXT: michael@0: return 3 * bitsPerComponent; michael@0: michael@0: case LOCAL_GL_RGBA: michael@0: case LOCAL_GL_RGBA32F: michael@0: case LOCAL_GL_SRGB_ALPHA_EXT: michael@0: return 4 * bitsPerComponent; michael@0: michael@0: // Compressed formats michael@0: case LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1: michael@0: case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1: michael@0: return 2; michael@0: michael@0: case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT: michael@0: case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: michael@0: case LOCAL_GL_ATC_RGB: michael@0: case LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1: michael@0: case LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1: michael@0: case LOCAL_GL_ETC1_RGB8_OES: michael@0: return 4; michael@0: michael@0: case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: michael@0: case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: michael@0: case LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA: michael@0: case LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA: michael@0: return 8; michael@0: michael@0: default: michael@0: break; michael@0: } michael@0: michael@0: MOZ_ASSERT(false, "Unhandled format+type combo."); michael@0: return 0; michael@0: } michael@0: michael@0: /** michael@0: * Perform validation of format/type combinations for TexImage variants. michael@0: * Returns true if the format/type is a valid combination, false otherwise. michael@0: */ michael@0: bool michael@0: WebGLContext::ValidateTexImageFormatAndType(GLenum format, GLenum type, WebGLTexImageFunc func) michael@0: { michael@0: if (!ValidateTexImageFormat(format, func) || michael@0: !ValidateTexImageType(type, func)) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: bool validCombo = false; michael@0: michael@0: switch (format) { michael@0: case LOCAL_GL_ALPHA: michael@0: case LOCAL_GL_LUMINANCE: michael@0: case LOCAL_GL_LUMINANCE_ALPHA: michael@0: validCombo = (type == LOCAL_GL_UNSIGNED_BYTE || michael@0: type == LOCAL_GL_HALF_FLOAT || michael@0: type == LOCAL_GL_HALF_FLOAT_OES || michael@0: type == LOCAL_GL_FLOAT); michael@0: break; michael@0: michael@0: case LOCAL_GL_RGB: michael@0: case LOCAL_GL_SRGB: michael@0: validCombo = (type == LOCAL_GL_UNSIGNED_BYTE || michael@0: type == LOCAL_GL_UNSIGNED_SHORT_5_6_5 || michael@0: type == LOCAL_GL_HALF_FLOAT || michael@0: type == LOCAL_GL_HALF_FLOAT_OES || michael@0: type == LOCAL_GL_FLOAT); michael@0: break; michael@0: michael@0: case LOCAL_GL_RGBA: michael@0: case LOCAL_GL_SRGB_ALPHA: michael@0: validCombo = (type == LOCAL_GL_UNSIGNED_BYTE || michael@0: type == LOCAL_GL_UNSIGNED_SHORT_4_4_4_4 || michael@0: type == LOCAL_GL_UNSIGNED_SHORT_5_5_5_1 || michael@0: type == LOCAL_GL_HALF_FLOAT || michael@0: type == LOCAL_GL_HALF_FLOAT_OES || michael@0: type == LOCAL_GL_FLOAT); michael@0: break; michael@0: michael@0: case LOCAL_GL_DEPTH_COMPONENT: michael@0: validCombo = (type == LOCAL_GL_UNSIGNED_SHORT || michael@0: type == LOCAL_GL_UNSIGNED_INT); michael@0: break; michael@0: michael@0: case LOCAL_GL_DEPTH_STENCIL: michael@0: validCombo = (type == LOCAL_GL_UNSIGNED_INT_24_8); michael@0: break; michael@0: michael@0: case LOCAL_GL_ATC_RGB: michael@0: case LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA: michael@0: case LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA: michael@0: case LOCAL_GL_ETC1_RGB8_OES: michael@0: case LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1: michael@0: case LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1: michael@0: case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1: michael@0: case LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1: michael@0: case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT: michael@0: case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: michael@0: case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: michael@0: case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: michael@0: validCombo = (type == LOCAL_GL_UNSIGNED_BYTE); michael@0: break; michael@0: michael@0: default: michael@0: // Only valid formats should be passed to the switch stmt. michael@0: MOZ_ASSERT(false, "Unexpected format and type combo. How'd this happen?"); michael@0: validCombo = false; michael@0: // Fall through to return an InvalidOperations. This will alert us to the michael@0: // unexpected case that needs fixing in builds without asserts. michael@0: } michael@0: michael@0: if (!validCombo) michael@0: ErrorInvalidOperation("%s: invalid combination of format %s and type %s", michael@0: InfoFrom(func), NameFrom(format), NameFrom(type)); michael@0: michael@0: return validCombo; michael@0: } michael@0: michael@0: /** michael@0: * Return true if format, type and jsArrayType are a valid combination. michael@0: * Also returns the size for texel of format and type (in bytes) via michael@0: * \a texelSize. michael@0: * michael@0: * It is assumed that type has previously been validated. michael@0: */ michael@0: bool michael@0: WebGLContext::ValidateTexInputData(GLenum type, int jsArrayType, WebGLTexImageFunc func) michael@0: { michael@0: bool validInput = false; michael@0: const char invalidTypedArray[] = "%s: invalid typed array type for given texture data type"; michael@0: michael@0: // First, we check for packed types michael@0: switch (type) { michael@0: case LOCAL_GL_UNSIGNED_BYTE: michael@0: validInput = (jsArrayType == -1 || jsArrayType == js::ArrayBufferView::TYPE_UINT8); michael@0: break; michael@0: michael@0: // TODO: WebGL spec doesn't allow half floats to specified as UInt16. michael@0: case LOCAL_GL_HALF_FLOAT: michael@0: case LOCAL_GL_HALF_FLOAT_OES: michael@0: validInput = (jsArrayType == -1); michael@0: break; michael@0: michael@0: case LOCAL_GL_UNSIGNED_SHORT: michael@0: case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4: michael@0: case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1: michael@0: case LOCAL_GL_UNSIGNED_SHORT_5_6_5: michael@0: validInput = (jsArrayType == -1 || jsArrayType == js::ArrayBufferView::TYPE_UINT16); michael@0: break; michael@0: michael@0: case LOCAL_GL_UNSIGNED_INT: michael@0: case LOCAL_GL_UNSIGNED_INT_24_8: michael@0: validInput = (jsArrayType == -1 || jsArrayType == js::ArrayBufferView::TYPE_UINT32); michael@0: break; michael@0: michael@0: case LOCAL_GL_FLOAT: michael@0: validInput = (jsArrayType == -1 || jsArrayType == js::ArrayBufferView::TYPE_FLOAT32); michael@0: break; michael@0: michael@0: default: michael@0: break; michael@0: } michael@0: michael@0: if (!validInput) michael@0: ErrorInvalidOperation(invalidTypedArray, InfoFrom(func)); michael@0: michael@0: return validInput; michael@0: } michael@0: michael@0: /** michael@0: * Test the gl(Copy|Compressed)?Tex[Sub]?Image[23]() parameters for errors. michael@0: * Verifies each of the parameters against the WebGL standard and enabled extensions. michael@0: */ michael@0: // TODO: Texture dims is here for future expansion in WebGL 2.0 michael@0: bool michael@0: WebGLContext::ValidateTexImage(GLuint dims, GLenum target, michael@0: GLint level, GLint internalFormat, michael@0: GLint xoffset, GLint yoffset, GLint zoffset, michael@0: GLint width, GLint height, GLint depth, michael@0: GLint border, GLenum format, GLenum type, michael@0: WebGLTexImageFunc func) michael@0: { michael@0: const char* info = InfoFrom(func); michael@0: michael@0: /* Check target */ michael@0: if (!ValidateTexImageTarget(dims, target, func)) michael@0: return false; michael@0: michael@0: /* Check level */ michael@0: if (level < 0) { michael@0: ErrorInvalidValue("%s: level must be >= 0", info); michael@0: return false; michael@0: } michael@0: michael@0: /* Check border */ michael@0: if (border != 0) { michael@0: ErrorInvalidValue("%s: border must be 0", info); michael@0: return false; michael@0: } michael@0: michael@0: /* Check incoming image format and type */ michael@0: if (!ValidateTexImageFormatAndType(format, type, func)) michael@0: return false; michael@0: michael@0: /* WebGL and OpenGL ES 2.0 impose additional restrictions on the michael@0: * combinations of format, internalFormat, and type that can be michael@0: * used. Formats and types that require additional extensions michael@0: * (e.g., GL_FLOAT requires GL_OES_texture_float) are filtered michael@0: * elsewhere. michael@0: */ michael@0: if ((GLint) format != internalFormat) { michael@0: ErrorInvalidOperation("%s: format does not match internalformat", info); michael@0: return false; michael@0: } michael@0: michael@0: /* check internalFormat */ michael@0: // TODO: Not sure if this is a bit of over kill. michael@0: if (BaseTexFormat(internalFormat) == LOCAL_GL_NONE) { michael@0: MOZ_ASSERT(false); michael@0: ErrorInvalidValue("%s:", info); michael@0: return false; michael@0: } michael@0: michael@0: /* Check texture image size */ michael@0: if (!ValidateTexImageSize(target, level, width, height, 0, func)) michael@0: return false; michael@0: michael@0: /* 5.14.8 Texture objects - WebGL Spec. michael@0: * "If an attempt is made to call these functions with no michael@0: * WebGLTexture bound (see above), an INVALID_OPERATION error michael@0: * is generated." michael@0: */ michael@0: WebGLTexture* tex = activeBoundTextureForTarget(target); michael@0: if (!tex) { michael@0: ErrorInvalidOperation("%s: no texture is bound to target %s", michael@0: info, NameFrom(target)); michael@0: return false; michael@0: } michael@0: michael@0: if (IsSubFunc(func)) { michael@0: if (!tex->HasImageInfoAt(target, level)) { michael@0: ErrorInvalidOperation("%s: no texture image previously defined for target %s at level %d", michael@0: info, NameFrom(target), level); michael@0: return false; michael@0: } michael@0: michael@0: const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(target, level); michael@0: if (!ValidateTexSubImageSize(xoffset, yoffset, zoffset, michael@0: width, height, depth, michael@0: imageInfo.Width(), imageInfo.Height(), 0, michael@0: func)) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: /* Require the format and type to match that of the existing michael@0: * texture as created michael@0: */ michael@0: if (imageInfo.WebGLFormat() != format || michael@0: imageInfo.WebGLType() != type) michael@0: { michael@0: ErrorInvalidOperation("%s: format or type doesn't match the existing texture", michael@0: info); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: /* Additional checks for depth textures */ michael@0: if (format == LOCAL_GL_DEPTH_COMPONENT || michael@0: format == LOCAL_GL_DEPTH_STENCIL) michael@0: { michael@0: if (func == WebGLTexImageFunc::TexSubImage || IsCopyFunc(func)) { michael@0: ErrorInvalidOperationWithName(this, "called with format/internalformat", michael@0: format, func); michael@0: return false; michael@0: } michael@0: michael@0: if (func == WebGLTexImageFunc::TexImage && michael@0: target != LOCAL_GL_TEXTURE_2D) michael@0: { michael@0: ErrorInvalidOperation("%s: with format of %s target must be TEXTURE_2D", michael@0: info, NameFrom(format)); michael@0: return false; michael@0: } michael@0: michael@0: if (func == WebGLTexImageFunc::TexImage && level != 0) { michael@0: ErrorInvalidOperation("%s: with format of %s target, level must be 0", michael@0: info, NameFrom(format)); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: /* Additional checks for compressed textures */ michael@0: if (!IsAllowedFromSource(format, func)) { michael@0: ErrorInvalidOperation("%s: Invalid format %s for this operation", michael@0: info, NameFrom(format)); michael@0: return false; michael@0: } michael@0: michael@0: /* Parameters are OK */ michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: WebGLContext::ValidateUniformLocation(const char* info, WebGLUniformLocation *location_object) michael@0: { michael@0: if (!ValidateObjectAllowNull(info, location_object)) michael@0: return false; michael@0: if (!location_object) michael@0: return false; michael@0: /* the need to check specifically for !mCurrentProgram here is explained in bug 657556 */ michael@0: if (!mCurrentProgram) { michael@0: ErrorInvalidOperation("%s: no program is currently bound", info); michael@0: return false; michael@0: } michael@0: if (mCurrentProgram != location_object->Program()) { michael@0: ErrorInvalidOperation("%s: this uniform location doesn't correspond to the current program", info); michael@0: return false; michael@0: } michael@0: if (mCurrentProgram->Generation() != location_object->ProgramGeneration()) { michael@0: ErrorInvalidOperation("%s: This uniform location is obsolete since the program has been relinked", info); michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: WebGLContext::ValidateSamplerUniformSetter(const char* info, WebGLUniformLocation *location, GLint value) michael@0: { michael@0: if (location->Info().type != SH_SAMPLER_2D && michael@0: location->Info().type != SH_SAMPLER_CUBE) michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: if (value >= 0 && value < mGLMaxTextureUnits) michael@0: return true; michael@0: michael@0: ErrorInvalidValue("%s: this uniform location is a sampler, but %d is not a valid texture unit", michael@0: info, value); michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: WebGLContext::ValidateAttribArraySetter(const char* name, uint32_t cnt, uint32_t arrayLength) michael@0: { michael@0: if (IsContextLost()) { michael@0: return false; michael@0: } michael@0: if (arrayLength < cnt) { michael@0: ErrorInvalidOperation("%s: array must be >= %d elements", name, cnt); michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: WebGLContext::ValidateUniformArraySetter(const char* name, uint32_t expectedElemSize, WebGLUniformLocation *location_object, michael@0: GLint& location, uint32_t& numElementsToUpload, uint32_t arrayLength) michael@0: { michael@0: if (IsContextLost()) michael@0: return false; michael@0: if (!ValidateUniformLocation(name, location_object)) michael@0: return false; michael@0: location = location_object->Location(); michael@0: uint32_t uniformElemSize = location_object->ElementSize(); michael@0: if (expectedElemSize != uniformElemSize) { michael@0: ErrorInvalidOperation("%s: this function expected a uniform of element size %d," michael@0: " got a uniform of element size %d", name, michael@0: expectedElemSize, michael@0: uniformElemSize); michael@0: return false; michael@0: } michael@0: if (arrayLength == 0 || michael@0: arrayLength % expectedElemSize) michael@0: { michael@0: ErrorInvalidValue("%s: expected an array of length a multiple" michael@0: " of %d, got an array of length %d", name, michael@0: expectedElemSize, michael@0: arrayLength); michael@0: return false; michael@0: } michael@0: const WebGLUniformInfo& info = location_object->Info(); michael@0: if (!info.isArray && michael@0: arrayLength != expectedElemSize) { michael@0: ErrorInvalidOperation("%s: expected an array of length exactly" michael@0: " %d (since this uniform is not an array" michael@0: " uniform), got an array of length %d", name, michael@0: expectedElemSize, michael@0: arrayLength); michael@0: return false; michael@0: } michael@0: numElementsToUpload = michael@0: std::min(info.arraySize, arrayLength / expectedElemSize); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: WebGLContext::ValidateUniformMatrixArraySetter(const char* name, int dim, WebGLUniformLocation *location_object, michael@0: GLint& location, uint32_t& numElementsToUpload, uint32_t arrayLength, michael@0: WebGLboolean aTranspose) michael@0: { michael@0: uint32_t expectedElemSize = (dim)*(dim); michael@0: if (IsContextLost()) michael@0: return false; michael@0: if (!ValidateUniformLocation(name, location_object)) michael@0: return false; michael@0: location = location_object->Location(); michael@0: uint32_t uniformElemSize = location_object->ElementSize(); michael@0: if (expectedElemSize != uniformElemSize) { michael@0: ErrorInvalidOperation("%s: this function expected a uniform of element size %d," michael@0: " got a uniform of element size %d", name, michael@0: expectedElemSize, michael@0: uniformElemSize); michael@0: return false; michael@0: } michael@0: if (arrayLength == 0 || michael@0: arrayLength % expectedElemSize) michael@0: { michael@0: ErrorInvalidValue("%s: expected an array of length a multiple" michael@0: " of %d, got an array of length %d", name, michael@0: expectedElemSize, michael@0: arrayLength); michael@0: return false; michael@0: } michael@0: const WebGLUniformInfo& info = location_object->Info(); michael@0: if (!info.isArray && michael@0: arrayLength != expectedElemSize) { michael@0: ErrorInvalidOperation("%s: expected an array of length exactly" michael@0: " %d (since this uniform is not an array" michael@0: " uniform), got an array of length %d", name, michael@0: expectedElemSize, michael@0: arrayLength); michael@0: return false; michael@0: } michael@0: if (aTranspose) { michael@0: ErrorInvalidValue("%s: transpose must be FALSE as per the " michael@0: "OpenGL ES 2.0 spec", name); michael@0: return false; michael@0: } michael@0: numElementsToUpload = michael@0: std::min(info.arraySize, arrayLength / (expectedElemSize)); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: WebGLContext::ValidateUniformSetter(const char* name, WebGLUniformLocation *location_object, GLint& location) michael@0: { michael@0: if (IsContextLost()) michael@0: return false; michael@0: if (!ValidateUniformLocation(name, location_object)) michael@0: return false; michael@0: location = location_object->Location(); michael@0: return true; michael@0: } michael@0: michael@0: bool WebGLContext::ValidateAttribIndex(GLuint index, const char *info) michael@0: { michael@0: return mBoundVertexArray->EnsureAttrib(index, info); michael@0: } michael@0: michael@0: bool WebGLContext::ValidateStencilParamsForDrawCall() michael@0: { michael@0: const char *msg = "%s set different front and back stencil %s. Drawing in this configuration is not allowed."; michael@0: if (mStencilRefFront != mStencilRefBack) { michael@0: ErrorInvalidOperation(msg, "stencilFuncSeparate", "reference values"); michael@0: return false; michael@0: } michael@0: if (mStencilValueMaskFront != mStencilValueMaskBack) { michael@0: ErrorInvalidOperation(msg, "stencilFuncSeparate", "value masks"); michael@0: return false; michael@0: } michael@0: if (mStencilWriteMaskFront != mStencilWriteMaskBack) { michael@0: ErrorInvalidOperation(msg, "stencilMaskSeparate", "write masks"); michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: static inline int32_t floorPOT(int32_t x) michael@0: { michael@0: MOZ_ASSERT(x > 0); michael@0: int32_t pot = 1; michael@0: while (pot < 0x40000000) { michael@0: if (x < pot*2) michael@0: break; michael@0: pot *= 2; michael@0: } michael@0: return pot; michael@0: } michael@0: michael@0: bool michael@0: WebGLContext::InitAndValidateGL() michael@0: { michael@0: if (!gl) return false; michael@0: michael@0: GLenum error = gl->fGetError(); michael@0: if (error != LOCAL_GL_NO_ERROR) { michael@0: GenerateWarning("GL error 0x%x occurred during OpenGL context initialization, before WebGL initialization!", error); michael@0: return false; michael@0: } michael@0: michael@0: mMinCapability = Preferences::GetBool("webgl.min_capability_mode", false); michael@0: mDisableExtensions = Preferences::GetBool("webgl.disable-extensions", false); michael@0: mLoseContextOnHeapMinimize = Preferences::GetBool("webgl.lose-context-on-heap-minimize", false); michael@0: mCanLoseContextInForeground = Preferences::GetBool("webgl.can-lose-context-in-foreground", true); michael@0: michael@0: if (MinCapabilityMode()) { michael@0: mDisableFragHighP = true; michael@0: } michael@0: michael@0: // These are the default values, see 6.2 State tables in the michael@0: // OpenGL ES 2.0.25 spec. michael@0: mColorWriteMask[0] = 1; michael@0: mColorWriteMask[1] = 1; michael@0: mColorWriteMask[2] = 1; michael@0: mColorWriteMask[3] = 1; michael@0: mDepthWriteMask = 1; michael@0: mColorClearValue[0] = 0.f; michael@0: mColorClearValue[1] = 0.f; michael@0: mColorClearValue[2] = 0.f; michael@0: mColorClearValue[3] = 0.f; michael@0: mDepthClearValue = 1.f; michael@0: mStencilClearValue = 0; michael@0: mStencilRefFront = 0; michael@0: mStencilRefBack = 0; michael@0: mStencilValueMaskFront = 0xffffffff; michael@0: mStencilValueMaskBack = 0xffffffff; michael@0: mStencilWriteMaskFront = 0xffffffff; michael@0: mStencilWriteMaskBack = 0xffffffff; michael@0: michael@0: // Bindings, etc. michael@0: mActiveTexture = 0; michael@0: mEmitContextLostErrorOnce = true; michael@0: mWebGLError = LOCAL_GL_NO_ERROR; michael@0: mUnderlyingGLError = LOCAL_GL_NO_ERROR; michael@0: michael@0: mBound2DTextures.Clear(); michael@0: mBoundCubeMapTextures.Clear(); michael@0: michael@0: mBoundArrayBuffer = nullptr; michael@0: mBoundTransformFeedbackBuffer = nullptr; michael@0: mCurrentProgram = nullptr; michael@0: michael@0: mBoundFramebuffer = nullptr; michael@0: mBoundRenderbuffer = nullptr; michael@0: michael@0: MakeContextCurrent(); michael@0: michael@0: // on desktop OpenGL, we always keep vertex attrib 0 array enabled michael@0: if (!gl->IsGLES()) { michael@0: gl->fEnableVertexAttribArray(0); michael@0: } michael@0: michael@0: if (MinCapabilityMode()) { michael@0: mGLMaxVertexAttribs = MINVALUE_GL_MAX_VERTEX_ATTRIBS; michael@0: } else { michael@0: gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_ATTRIBS, &mGLMaxVertexAttribs); michael@0: } michael@0: if (mGLMaxVertexAttribs < 8) { michael@0: GenerateWarning("GL_MAX_VERTEX_ATTRIBS: %d is < 8!", mGLMaxVertexAttribs); michael@0: return false; michael@0: } michael@0: michael@0: // Note: GL_MAX_TEXTURE_UNITS is fixed at 4 for most desktop hardware, michael@0: // even though the hardware supports much more. The michael@0: // GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS value is the accurate value. michael@0: if (MinCapabilityMode()) { michael@0: mGLMaxTextureUnits = MINVALUE_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS; michael@0: } else { michael@0: gl->fGetIntegerv(LOCAL_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &mGLMaxTextureUnits); michael@0: } michael@0: if (mGLMaxTextureUnits < 8) { michael@0: GenerateWarning("GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: %d is < 8!", mGLMaxTextureUnits); michael@0: return false; michael@0: } michael@0: michael@0: mBound2DTextures.SetLength(mGLMaxTextureUnits); michael@0: mBoundCubeMapTextures.SetLength(mGLMaxTextureUnits); michael@0: michael@0: if (MinCapabilityMode()) { michael@0: mGLMaxTextureSize = MINVALUE_GL_MAX_TEXTURE_SIZE; michael@0: mGLMaxCubeMapTextureSize = MINVALUE_GL_MAX_CUBE_MAP_TEXTURE_SIZE; michael@0: mGLMaxRenderbufferSize = MINVALUE_GL_MAX_RENDERBUFFER_SIZE; michael@0: mGLMaxTextureImageUnits = MINVALUE_GL_MAX_TEXTURE_IMAGE_UNITS; michael@0: mGLMaxVertexTextureImageUnits = MINVALUE_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS; michael@0: } else { michael@0: gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, &mGLMaxTextureSize); michael@0: gl->fGetIntegerv(LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE, &mGLMaxCubeMapTextureSize); michael@0: gl->fGetIntegerv(LOCAL_GL_MAX_RENDERBUFFER_SIZE, &mGLMaxRenderbufferSize); michael@0: gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS, &mGLMaxTextureImageUnits); michael@0: gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &mGLMaxVertexTextureImageUnits); michael@0: } michael@0: michael@0: mGLMaxTextureSize = floorPOT(mGLMaxTextureSize); michael@0: mGLMaxRenderbufferSize = floorPOT(mGLMaxRenderbufferSize); michael@0: michael@0: if (MinCapabilityMode()) { michael@0: mGLMaxFragmentUniformVectors = MINVALUE_GL_MAX_FRAGMENT_UNIFORM_VECTORS; michael@0: mGLMaxVertexUniformVectors = MINVALUE_GL_MAX_VERTEX_UNIFORM_VECTORS; michael@0: mGLMaxVaryingVectors = MINVALUE_GL_MAX_VARYING_VECTORS; michael@0: } else { michael@0: if (gl->IsSupported(gl::GLFeature::ES2_compatibility)) { michael@0: gl->fGetIntegerv(LOCAL_GL_MAX_FRAGMENT_UNIFORM_VECTORS, &mGLMaxFragmentUniformVectors); michael@0: gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_UNIFORM_VECTORS, &mGLMaxVertexUniformVectors); michael@0: gl->fGetIntegerv(LOCAL_GL_MAX_VARYING_VECTORS, &mGLMaxVaryingVectors); michael@0: } else { michael@0: gl->fGetIntegerv(LOCAL_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &mGLMaxFragmentUniformVectors); michael@0: mGLMaxFragmentUniformVectors /= 4; michael@0: gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_UNIFORM_COMPONENTS, &mGLMaxVertexUniformVectors); michael@0: mGLMaxVertexUniformVectors /= 4; michael@0: michael@0: // we are now going to try to read GL_MAX_VERTEX_OUTPUT_COMPONENTS and GL_MAX_FRAGMENT_INPUT_COMPONENTS, michael@0: // however these constants only entered the OpenGL standard at OpenGL 3.2. So we will try reading, michael@0: // and check OpenGL error for INVALID_ENUM. michael@0: michael@0: // before we start, we check that no error already occurred, to prevent hiding it in our subsequent error handling michael@0: error = gl->GetAndClearError(); michael@0: if (error != LOCAL_GL_NO_ERROR) { michael@0: GenerateWarning("GL error 0x%x occurred during WebGL context initialization!", error); michael@0: return false; michael@0: } michael@0: michael@0: // On the public_webgl list, "problematic GetParameter pnames" thread, the following formula was given: michael@0: // mGLMaxVaryingVectors = min (GL_MAX_VERTEX_OUTPUT_COMPONENTS, GL_MAX_FRAGMENT_INPUT_COMPONENTS) / 4 michael@0: GLint maxVertexOutputComponents, michael@0: minFragmentInputComponents; michael@0: gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_OUTPUT_COMPONENTS, &maxVertexOutputComponents); michael@0: gl->fGetIntegerv(LOCAL_GL_MAX_FRAGMENT_INPUT_COMPONENTS, &minFragmentInputComponents); michael@0: michael@0: error = gl->GetAndClearError(); michael@0: switch (error) { michael@0: case LOCAL_GL_NO_ERROR: michael@0: mGLMaxVaryingVectors = std::min(maxVertexOutputComponents, minFragmentInputComponents) / 4; michael@0: break; michael@0: case LOCAL_GL_INVALID_ENUM: michael@0: mGLMaxVaryingVectors = 16; // = 64/4, 64 is the min value for maxVertexOutputComponents in OpenGL 3.2 spec michael@0: break; michael@0: default: michael@0: GenerateWarning("GL error 0x%x occurred during WebGL context initialization!", error); michael@0: return false; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Always 1 for GLES2 michael@0: mMaxFramebufferColorAttachments = 1; michael@0: michael@0: if (!gl->IsGLES()) { michael@0: // gl_PointSize is always available in ES2 GLSL, but has to be michael@0: // specifically enabled on desktop GLSL. michael@0: gl->fEnable(LOCAL_GL_VERTEX_PROGRAM_POINT_SIZE); michael@0: michael@0: // gl_PointCoord is always available in ES2 GLSL and in newer desktop GLSL versions, but apparently michael@0: // not in OpenGL 2 and apparently not (due to a driver bug) on certain NVIDIA setups. See: michael@0: // http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=261472 michael@0: // Note that this used to cause crashes on old ATI drivers... hopefully not a significant michael@0: // problem anymore. See bug 602183. michael@0: gl->fEnable(LOCAL_GL_POINT_SPRITE); michael@0: } michael@0: michael@0: #ifdef XP_MACOSX michael@0: if (gl->WorkAroundDriverBugs() && michael@0: gl->Vendor() == gl::GLVendor::ATI) { michael@0: // The Mac ATI driver, in all known OSX version up to and including 10.8, michael@0: // renders points sprites upside-down. Apple bug 11778921 michael@0: gl->fPointParameterf(LOCAL_GL_POINT_SPRITE_COORD_ORIGIN, LOCAL_GL_LOWER_LEFT); michael@0: } michael@0: #endif michael@0: michael@0: // Check the shader validator pref michael@0: NS_ENSURE_TRUE(Preferences::GetRootBranch(), false); michael@0: michael@0: mShaderValidation = michael@0: Preferences::GetBool("webgl.shader_validator", mShaderValidation); michael@0: michael@0: // initialize shader translator michael@0: if (mShaderValidation) { michael@0: if (!ShInitialize()) { michael@0: GenerateWarning("GLSL translator initialization failed!"); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: // Mesa can only be detected with the GL_VERSION string, of the form "2.1 Mesa 7.11.0" michael@0: mIsMesa = strstr((const char *)(gl->fGetString(LOCAL_GL_VERSION)), "Mesa"); michael@0: michael@0: // notice that the point of calling GetAndClearError here is not only to check for error, michael@0: // it is also to reset the error flags so that a subsequent WebGL getError call will give the correct result. michael@0: error = gl->GetAndClearError(); michael@0: if (error != LOCAL_GL_NO_ERROR) { michael@0: GenerateWarning("GL error 0x%x occurred during WebGL context initialization!", error); michael@0: return false; michael@0: } michael@0: michael@0: if (IsWebGL2() && michael@0: !InitWebGL2()) michael@0: { michael@0: // Todo: Bug 898404: Only allow WebGL2 on GL>=3.0 on desktop GL. michael@0: return false; michael@0: } michael@0: michael@0: mMemoryPressureObserver michael@0: = new WebGLMemoryPressureObserver(this); michael@0: nsCOMPtr observerService michael@0: = mozilla::services::GetObserverService(); michael@0: if (observerService) { michael@0: observerService->AddObserver(mMemoryPressureObserver, michael@0: "memory-pressure", michael@0: false); michael@0: } michael@0: michael@0: mDefaultVertexArray = new WebGLVertexArray(this); michael@0: mDefaultVertexArray->mAttribs.SetLength(mGLMaxVertexAttribs); michael@0: mBoundVertexArray = mDefaultVertexArray; michael@0: michael@0: return true; michael@0: }