Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #include "WebGLContext.h" |
michael@0 | 7 | #include "WebGLBuffer.h" |
michael@0 | 8 | #include "WebGLVertexAttribData.h" |
michael@0 | 9 | #include "WebGLShader.h" |
michael@0 | 10 | #include "WebGLProgram.h" |
michael@0 | 11 | #include "WebGLUniformLocation.h" |
michael@0 | 12 | #include "WebGLFramebuffer.h" |
michael@0 | 13 | #include "WebGLRenderbuffer.h" |
michael@0 | 14 | #include "WebGLTexture.h" |
michael@0 | 15 | #include "WebGLVertexArray.h" |
michael@0 | 16 | #include "GLContext.h" |
michael@0 | 17 | #include "CanvasUtils.h" |
michael@0 | 18 | |
michael@0 | 19 | #include "mozilla/CheckedInt.h" |
michael@0 | 20 | #include "mozilla/Preferences.h" |
michael@0 | 21 | #include "mozilla/Services.h" |
michael@0 | 22 | |
michael@0 | 23 | #include "jsfriendapi.h" |
michael@0 | 24 | |
michael@0 | 25 | #include "angle/ShaderLang.h" |
michael@0 | 26 | |
michael@0 | 27 | #include <algorithm> |
michael@0 | 28 | |
michael@0 | 29 | #include "mozilla/Services.h" |
michael@0 | 30 | #include "nsIObserverService.h" |
michael@0 | 31 | |
michael@0 | 32 | using namespace mozilla; |
michael@0 | 33 | |
michael@0 | 34 | /** |
michael@0 | 35 | * Return the block size for format. |
michael@0 | 36 | */ |
michael@0 | 37 | static void |
michael@0 | 38 | BlockSizeFor(GLenum format, GLint* blockWidth, GLint* blockHeight) |
michael@0 | 39 | { |
michael@0 | 40 | MOZ_ASSERT(blockWidth && blockHeight); |
michael@0 | 41 | |
michael@0 | 42 | switch (format) { |
michael@0 | 43 | case LOCAL_GL_ATC_RGB: |
michael@0 | 44 | case LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA: |
michael@0 | 45 | case LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA: |
michael@0 | 46 | case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT: |
michael@0 | 47 | case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: |
michael@0 | 48 | case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: |
michael@0 | 49 | case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: |
michael@0 | 50 | if (blockWidth) |
michael@0 | 51 | *blockWidth = 4; |
michael@0 | 52 | if (blockHeight) |
michael@0 | 53 | *blockHeight = 4; |
michael@0 | 54 | break; |
michael@0 | 55 | |
michael@0 | 56 | case LOCAL_GL_ETC1_RGB8_OES: |
michael@0 | 57 | // 4x4 blocks, but no 4-multiple requirement. |
michael@0 | 58 | default: |
michael@0 | 59 | break; |
michael@0 | 60 | } |
michael@0 | 61 | } |
michael@0 | 62 | |
michael@0 | 63 | /** |
michael@0 | 64 | * Return the displayable name for the texture function that is the |
michael@0 | 65 | * source for validation. |
michael@0 | 66 | */ |
michael@0 | 67 | static const char* |
michael@0 | 68 | InfoFrom(WebGLTexImageFunc func) |
michael@0 | 69 | { |
michael@0 | 70 | // TODO: Account for dimensions (WebGL 2) |
michael@0 | 71 | switch (func) { |
michael@0 | 72 | case WebGLTexImageFunc::TexImage: return "texImage2D"; |
michael@0 | 73 | case WebGLTexImageFunc::TexSubImage: return "texSubImage2D"; |
michael@0 | 74 | case WebGLTexImageFunc::CopyTexImage: return "copyTexImage2D"; |
michael@0 | 75 | case WebGLTexImageFunc::CopyTexSubImage: return "copyTexSubImage2D"; |
michael@0 | 76 | case WebGLTexImageFunc::CompTexImage: return "compressedTexImage2D"; |
michael@0 | 77 | case WebGLTexImageFunc::CompTexSubImage: return "compressedTexSubImage2D"; |
michael@0 | 78 | default: |
michael@0 | 79 | MOZ_ASSERT(false, "Missing case for WebGLTexImageSource"); |
michael@0 | 80 | return "(error)"; |
michael@0 | 81 | } |
michael@0 | 82 | } |
michael@0 | 83 | |
michael@0 | 84 | /** |
michael@0 | 85 | * Return displayable name for GLenum. |
michael@0 | 86 | * This version is like gl::GLenumToStr but with out the GL_ prefix to |
michael@0 | 87 | * keep consistency with how errors are reported from WebGL. |
michael@0 | 88 | */ |
michael@0 | 89 | static const char* |
michael@0 | 90 | NameFrom(GLenum glenum) |
michael@0 | 91 | { |
michael@0 | 92 | switch (glenum) { |
michael@0 | 93 | #define XX(x) case LOCAL_GL_##x: return #x |
michael@0 | 94 | XX(ALPHA); |
michael@0 | 95 | XX(ATC_RGB); |
michael@0 | 96 | XX(ATC_RGBA_EXPLICIT_ALPHA); |
michael@0 | 97 | XX(ATC_RGBA_INTERPOLATED_ALPHA); |
michael@0 | 98 | XX(COMPRESSED_RGBA_PVRTC_2BPPV1); |
michael@0 | 99 | XX(COMPRESSED_RGBA_PVRTC_4BPPV1); |
michael@0 | 100 | XX(COMPRESSED_RGBA_S3TC_DXT1_EXT); |
michael@0 | 101 | XX(COMPRESSED_RGBA_S3TC_DXT3_EXT); |
michael@0 | 102 | XX(COMPRESSED_RGBA_S3TC_DXT5_EXT); |
michael@0 | 103 | XX(COMPRESSED_RGB_PVRTC_2BPPV1); |
michael@0 | 104 | XX(COMPRESSED_RGB_PVRTC_4BPPV1); |
michael@0 | 105 | XX(COMPRESSED_RGB_S3TC_DXT1_EXT); |
michael@0 | 106 | XX(DEPTH_COMPONENT); |
michael@0 | 107 | XX(DEPTH_COMPONENT16); |
michael@0 | 108 | XX(DEPTH_COMPONENT32); |
michael@0 | 109 | XX(DEPTH_STENCIL); |
michael@0 | 110 | XX(DEPTH24_STENCIL8); |
michael@0 | 111 | XX(ETC1_RGB8_OES); |
michael@0 | 112 | XX(FLOAT); |
michael@0 | 113 | XX(HALF_FLOAT); |
michael@0 | 114 | XX(LUMINANCE); |
michael@0 | 115 | XX(LUMINANCE_ALPHA); |
michael@0 | 116 | XX(RGB); |
michael@0 | 117 | XX(RGB16F); |
michael@0 | 118 | XX(RGB32F); |
michael@0 | 119 | XX(RGBA); |
michael@0 | 120 | XX(RGBA16F); |
michael@0 | 121 | XX(RGBA32F); |
michael@0 | 122 | XX(SRGB); |
michael@0 | 123 | XX(SRGB_ALPHA); |
michael@0 | 124 | XX(TEXTURE_2D); |
michael@0 | 125 | XX(TEXTURE_3D); |
michael@0 | 126 | XX(TEXTURE_CUBE_MAP); |
michael@0 | 127 | XX(TEXTURE_CUBE_MAP_NEGATIVE_X); |
michael@0 | 128 | XX(TEXTURE_CUBE_MAP_NEGATIVE_Y); |
michael@0 | 129 | XX(TEXTURE_CUBE_MAP_NEGATIVE_Z); |
michael@0 | 130 | XX(TEXTURE_CUBE_MAP_POSITIVE_X); |
michael@0 | 131 | XX(TEXTURE_CUBE_MAP_POSITIVE_Y); |
michael@0 | 132 | XX(TEXTURE_CUBE_MAP_POSITIVE_Z); |
michael@0 | 133 | XX(UNSIGNED_BYTE); |
michael@0 | 134 | XX(UNSIGNED_INT); |
michael@0 | 135 | XX(UNSIGNED_INT_24_8); |
michael@0 | 136 | XX(UNSIGNED_SHORT); |
michael@0 | 137 | XX(UNSIGNED_SHORT_4_4_4_4); |
michael@0 | 138 | XX(UNSIGNED_SHORT_5_5_5_1); |
michael@0 | 139 | XX(UNSIGNED_SHORT_5_6_5); |
michael@0 | 140 | #undef XX |
michael@0 | 141 | } |
michael@0 | 142 | |
michael@0 | 143 | return nullptr; |
michael@0 | 144 | } |
michael@0 | 145 | |
michael@0 | 146 | /** |
michael@0 | 147 | * Same as ErrorInvalidEnum but uses NameFrom to print displayable |
michael@0 | 148 | * name for \a glenum. |
michael@0 | 149 | */ |
michael@0 | 150 | static void |
michael@0 | 151 | ErrorInvalidEnumWithName(WebGLContext* ctx, const char* msg, GLenum glenum, WebGLTexImageFunc func) |
michael@0 | 152 | { |
michael@0 | 153 | const char* name = NameFrom(glenum); |
michael@0 | 154 | if (name) |
michael@0 | 155 | ctx->ErrorInvalidEnum("%s: %s %s", InfoFrom(func), msg, name); |
michael@0 | 156 | else |
michael@0 | 157 | ctx->ErrorInvalidEnum("%s: %s 0x%04X", InfoFrom(func), msg, glenum); |
michael@0 | 158 | } |
michael@0 | 159 | |
michael@0 | 160 | /** |
michael@0 | 161 | * Same as ErrorInvalidOperation but uses NameFrom to print displayable |
michael@0 | 162 | * name for \a glenum. |
michael@0 | 163 | */ |
michael@0 | 164 | static void |
michael@0 | 165 | ErrorInvalidOperationWithName(WebGLContext* ctx, const char* msg, GLenum glenum, |
michael@0 | 166 | WebGLTexImageFunc func) |
michael@0 | 167 | { |
michael@0 | 168 | const char* name = NameFrom(glenum); |
michael@0 | 169 | if (name) |
michael@0 | 170 | ctx->ErrorInvalidOperation("%s: %s %s", InfoFrom(func), msg, name); |
michael@0 | 171 | else |
michael@0 | 172 | ctx->ErrorInvalidOperation("%s: %s 0x%04X", InfoFrom(func), msg, glenum); |
michael@0 | 173 | } |
michael@0 | 174 | |
michael@0 | 175 | /** |
michael@0 | 176 | * Return true if the format is valid for source calls. |
michael@0 | 177 | */ |
michael@0 | 178 | static bool |
michael@0 | 179 | IsAllowedFromSource(GLenum format, WebGLTexImageFunc func) |
michael@0 | 180 | { |
michael@0 | 181 | switch (format) { |
michael@0 | 182 | case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT: |
michael@0 | 183 | case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: |
michael@0 | 184 | case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: |
michael@0 | 185 | case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: |
michael@0 | 186 | case LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1: |
michael@0 | 187 | case LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1: |
michael@0 | 188 | case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1: |
michael@0 | 189 | case LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1: |
michael@0 | 190 | return (func == WebGLTexImageFunc::CompTexImage || |
michael@0 | 191 | func == WebGLTexImageFunc::CompTexSubImage); |
michael@0 | 192 | |
michael@0 | 193 | case LOCAL_GL_ATC_RGB: |
michael@0 | 194 | case LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA: |
michael@0 | 195 | case LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA: |
michael@0 | 196 | case LOCAL_GL_ETC1_RGB8_OES: |
michael@0 | 197 | return func == WebGLTexImageFunc::CompTexImage; |
michael@0 | 198 | } |
michael@0 | 199 | |
michael@0 | 200 | return true; |
michael@0 | 201 | } |
michael@0 | 202 | |
michael@0 | 203 | /** |
michael@0 | 204 | * Returns true if func is a CopyTexImage variant. |
michael@0 | 205 | */ |
michael@0 | 206 | static bool |
michael@0 | 207 | IsCopyFunc(WebGLTexImageFunc func) |
michael@0 | 208 | { |
michael@0 | 209 | return (func == WebGLTexImageFunc::CopyTexImage || |
michael@0 | 210 | func == WebGLTexImageFunc::CopyTexSubImage); |
michael@0 | 211 | } |
michael@0 | 212 | |
michael@0 | 213 | /** |
michael@0 | 214 | * Returns true if func is a SubImage variant. |
michael@0 | 215 | */ |
michael@0 | 216 | static bool |
michael@0 | 217 | IsSubFunc(WebGLTexImageFunc func) |
michael@0 | 218 | { |
michael@0 | 219 | return (func == WebGLTexImageFunc::TexSubImage || |
michael@0 | 220 | func == WebGLTexImageFunc::CopyTexSubImage || |
michael@0 | 221 | func == WebGLTexImageFunc::CompTexSubImage); |
michael@0 | 222 | } |
michael@0 | 223 | |
michael@0 | 224 | /** |
michael@0 | 225 | * returns true is target is a texture cube map target. |
michael@0 | 226 | */ |
michael@0 | 227 | static bool |
michael@0 | 228 | IsTexImageCubemapTarget(GLenum target) |
michael@0 | 229 | { |
michael@0 | 230 | return (target >= LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X && |
michael@0 | 231 | target <= LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z); |
michael@0 | 232 | } |
michael@0 | 233 | |
michael@0 | 234 | /* |
michael@0 | 235 | * Pull data out of the program, post-linking |
michael@0 | 236 | */ |
michael@0 | 237 | bool |
michael@0 | 238 | WebGLProgram::UpdateInfo() |
michael@0 | 239 | { |
michael@0 | 240 | mIdentifierMap = nullptr; |
michael@0 | 241 | mIdentifierReverseMap = nullptr; |
michael@0 | 242 | mUniformInfoMap = nullptr; |
michael@0 | 243 | |
michael@0 | 244 | mAttribMaxNameLength = 0; |
michael@0 | 245 | |
michael@0 | 246 | for (size_t i = 0; i < mAttachedShaders.Length(); i++) |
michael@0 | 247 | mAttribMaxNameLength = std::max(mAttribMaxNameLength, mAttachedShaders[i]->mAttribMaxNameLength); |
michael@0 | 248 | |
michael@0 | 249 | GLint attribCount; |
michael@0 | 250 | mContext->gl->fGetProgramiv(mGLName, LOCAL_GL_ACTIVE_ATTRIBUTES, &attribCount); |
michael@0 | 251 | |
michael@0 | 252 | if (!mAttribsInUse.SetLength(mContext->mGLMaxVertexAttribs)) { |
michael@0 | 253 | mContext->ErrorOutOfMemory("updateInfo: out of memory to allocate %d attribs", mContext->mGLMaxVertexAttribs); |
michael@0 | 254 | return false; |
michael@0 | 255 | } |
michael@0 | 256 | |
michael@0 | 257 | for (size_t i = 0; i < mAttribsInUse.Length(); i++) |
michael@0 | 258 | mAttribsInUse[i] = false; |
michael@0 | 259 | |
michael@0 | 260 | nsAutoArrayPtr<char> nameBuf(new char[mAttribMaxNameLength]); |
michael@0 | 261 | |
michael@0 | 262 | for (int i = 0; i < attribCount; ++i) { |
michael@0 | 263 | GLint attrnamelen; |
michael@0 | 264 | GLint attrsize; |
michael@0 | 265 | GLenum attrtype; |
michael@0 | 266 | mContext->gl->fGetActiveAttrib(mGLName, i, mAttribMaxNameLength, &attrnamelen, &attrsize, &attrtype, nameBuf); |
michael@0 | 267 | if (attrnamelen > 0) { |
michael@0 | 268 | GLint loc = mContext->gl->fGetAttribLocation(mGLName, nameBuf); |
michael@0 | 269 | MOZ_ASSERT(loc >= 0, "major oops in managing the attributes of a WebGL program"); |
michael@0 | 270 | if (loc < mContext->mGLMaxVertexAttribs) { |
michael@0 | 271 | mAttribsInUse[loc] = true; |
michael@0 | 272 | } else { |
michael@0 | 273 | mContext->GenerateWarning("program exceeds MAX_VERTEX_ATTRIBS"); |
michael@0 | 274 | return false; |
michael@0 | 275 | } |
michael@0 | 276 | } |
michael@0 | 277 | } |
michael@0 | 278 | |
michael@0 | 279 | if (!mUniformInfoMap) { |
michael@0 | 280 | mUniformInfoMap = new CStringToUniformInfoMap; |
michael@0 | 281 | for (size_t i = 0; i < mAttachedShaders.Length(); i++) { |
michael@0 | 282 | for (size_t j = 0; j < mAttachedShaders[i]->mUniforms.Length(); j++) { |
michael@0 | 283 | const WebGLMappedIdentifier& uniform = mAttachedShaders[i]->mUniforms[j]; |
michael@0 | 284 | const WebGLUniformInfo& info = mAttachedShaders[i]->mUniformInfos[j]; |
michael@0 | 285 | mUniformInfoMap->Put(uniform.mapped, info); |
michael@0 | 286 | } |
michael@0 | 287 | } |
michael@0 | 288 | } |
michael@0 | 289 | |
michael@0 | 290 | mActiveAttribMap.clear(); |
michael@0 | 291 | |
michael@0 | 292 | GLint numActiveAttrs = 0; |
michael@0 | 293 | mContext->gl->fGetProgramiv(mGLName, LOCAL_GL_ACTIVE_ATTRIBUTES, &numActiveAttrs); |
michael@0 | 294 | |
michael@0 | 295 | // Spec says the maximum attrib name length is 256 chars, so this is |
michael@0 | 296 | // sufficient to hold any attrib name. |
michael@0 | 297 | char attrName[257]; |
michael@0 | 298 | |
michael@0 | 299 | GLint dummySize; |
michael@0 | 300 | GLenum dummyType; |
michael@0 | 301 | for (GLint i = 0; i < numActiveAttrs; i++) { |
michael@0 | 302 | mContext->gl->fGetActiveAttrib(mGLName, i, 257, nullptr, &dummySize, |
michael@0 | 303 | &dummyType, attrName); |
michael@0 | 304 | GLint attrLoc = mContext->gl->fGetAttribLocation(mGLName, attrName); |
michael@0 | 305 | MOZ_ASSERT(attrLoc >= 0); |
michael@0 | 306 | mActiveAttribMap.insert(std::make_pair(attrLoc, nsCString(attrName))); |
michael@0 | 307 | } |
michael@0 | 308 | |
michael@0 | 309 | return true; |
michael@0 | 310 | } |
michael@0 | 311 | |
michael@0 | 312 | /** |
michael@0 | 313 | * Return the simple base format for a given internal format. |
michael@0 | 314 | * |
michael@0 | 315 | * \return the corresponding \u base internal format (GL_ALPHA, GL_LUMINANCE, |
michael@0 | 316 | * GL_LUMINANCE_ALPHA, GL_RGB, GL_RGBA), or GL_NONE if invalid enum. |
michael@0 | 317 | */ |
michael@0 | 318 | GLenum |
michael@0 | 319 | WebGLContext::BaseTexFormat(GLenum internalFormat) const |
michael@0 | 320 | { |
michael@0 | 321 | if (internalFormat == LOCAL_GL_ALPHA || |
michael@0 | 322 | internalFormat == LOCAL_GL_LUMINANCE || |
michael@0 | 323 | internalFormat == LOCAL_GL_LUMINANCE_ALPHA || |
michael@0 | 324 | internalFormat == LOCAL_GL_RGB || |
michael@0 | 325 | internalFormat == LOCAL_GL_RGBA) |
michael@0 | 326 | { |
michael@0 | 327 | return internalFormat; |
michael@0 | 328 | } |
michael@0 | 329 | |
michael@0 | 330 | if (IsExtensionEnabled(WebGLExtensionID::EXT_sRGB)) { |
michael@0 | 331 | if (internalFormat == LOCAL_GL_SRGB) |
michael@0 | 332 | return LOCAL_GL_RGB; |
michael@0 | 333 | |
michael@0 | 334 | if (internalFormat == LOCAL_GL_SRGB_ALPHA) |
michael@0 | 335 | return LOCAL_GL_RGBA; |
michael@0 | 336 | } |
michael@0 | 337 | |
michael@0 | 338 | if (IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_atc)) { |
michael@0 | 339 | if (internalFormat == LOCAL_GL_ATC_RGB) |
michael@0 | 340 | return LOCAL_GL_RGB; |
michael@0 | 341 | |
michael@0 | 342 | if (internalFormat == LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA || |
michael@0 | 343 | internalFormat == LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA) |
michael@0 | 344 | { |
michael@0 | 345 | return LOCAL_GL_RGBA; |
michael@0 | 346 | } |
michael@0 | 347 | } |
michael@0 | 348 | |
michael@0 | 349 | if (IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_etc1)) { |
michael@0 | 350 | if (internalFormat == LOCAL_GL_ETC1_RGB8_OES) |
michael@0 | 351 | return LOCAL_GL_RGB; |
michael@0 | 352 | } |
michael@0 | 353 | |
michael@0 | 354 | if (IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_pvrtc)) { |
michael@0 | 355 | if (internalFormat == LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1 || |
michael@0 | 356 | internalFormat == LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1) |
michael@0 | 357 | { |
michael@0 | 358 | return LOCAL_GL_RGB; |
michael@0 | 359 | } |
michael@0 | 360 | |
michael@0 | 361 | if (internalFormat == LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1 || |
michael@0 | 362 | internalFormat == LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1) |
michael@0 | 363 | { |
michael@0 | 364 | return LOCAL_GL_RGBA; |
michael@0 | 365 | } |
michael@0 | 366 | } |
michael@0 | 367 | |
michael@0 | 368 | if (IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_s3tc)) { |
michael@0 | 369 | if (internalFormat == LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT) |
michael@0 | 370 | return LOCAL_GL_RGB; |
michael@0 | 371 | |
michael@0 | 372 | if (internalFormat == LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT || |
michael@0 | 373 | internalFormat == LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT || |
michael@0 | 374 | internalFormat == LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT) |
michael@0 | 375 | { |
michael@0 | 376 | return LOCAL_GL_RGBA; |
michael@0 | 377 | } |
michael@0 | 378 | } |
michael@0 | 379 | |
michael@0 | 380 | if (IsExtensionEnabled(WebGLExtensionID::WEBGL_depth_texture)) { |
michael@0 | 381 | if (internalFormat == LOCAL_GL_DEPTH_COMPONENT || |
michael@0 | 382 | internalFormat == LOCAL_GL_DEPTH_COMPONENT16 || |
michael@0 | 383 | internalFormat == LOCAL_GL_DEPTH_COMPONENT32) |
michael@0 | 384 | { |
michael@0 | 385 | return LOCAL_GL_DEPTH_COMPONENT; |
michael@0 | 386 | } |
michael@0 | 387 | |
michael@0 | 388 | if (internalFormat == LOCAL_GL_DEPTH_STENCIL || |
michael@0 | 389 | internalFormat == LOCAL_GL_DEPTH24_STENCIL8) |
michael@0 | 390 | { |
michael@0 | 391 | return LOCAL_GL_DEPTH_STENCIL; |
michael@0 | 392 | } |
michael@0 | 393 | } |
michael@0 | 394 | |
michael@0 | 395 | MOZ_ASSERT(false, "Unhandled internalFormat"); |
michael@0 | 396 | return LOCAL_GL_NONE; |
michael@0 | 397 | } |
michael@0 | 398 | |
michael@0 | 399 | bool WebGLContext::ValidateBlendEquationEnum(GLenum mode, const char *info) |
michael@0 | 400 | { |
michael@0 | 401 | switch (mode) { |
michael@0 | 402 | case LOCAL_GL_FUNC_ADD: |
michael@0 | 403 | case LOCAL_GL_FUNC_SUBTRACT: |
michael@0 | 404 | case LOCAL_GL_FUNC_REVERSE_SUBTRACT: |
michael@0 | 405 | return true; |
michael@0 | 406 | case LOCAL_GL_MIN: |
michael@0 | 407 | case LOCAL_GL_MAX: |
michael@0 | 408 | if (IsWebGL2()) { |
michael@0 | 409 | // http://www.opengl.org/registry/specs/EXT/blend_minmax.txt |
michael@0 | 410 | return true; |
michael@0 | 411 | } |
michael@0 | 412 | break; |
michael@0 | 413 | default: |
michael@0 | 414 | break; |
michael@0 | 415 | } |
michael@0 | 416 | |
michael@0 | 417 | ErrorInvalidEnumInfo(info, mode); |
michael@0 | 418 | return false; |
michael@0 | 419 | } |
michael@0 | 420 | |
michael@0 | 421 | bool WebGLContext::ValidateBlendFuncDstEnum(GLenum factor, const char *info) |
michael@0 | 422 | { |
michael@0 | 423 | switch (factor) { |
michael@0 | 424 | case LOCAL_GL_ZERO: |
michael@0 | 425 | case LOCAL_GL_ONE: |
michael@0 | 426 | case LOCAL_GL_SRC_COLOR: |
michael@0 | 427 | case LOCAL_GL_ONE_MINUS_SRC_COLOR: |
michael@0 | 428 | case LOCAL_GL_DST_COLOR: |
michael@0 | 429 | case LOCAL_GL_ONE_MINUS_DST_COLOR: |
michael@0 | 430 | case LOCAL_GL_SRC_ALPHA: |
michael@0 | 431 | case LOCAL_GL_ONE_MINUS_SRC_ALPHA: |
michael@0 | 432 | case LOCAL_GL_DST_ALPHA: |
michael@0 | 433 | case LOCAL_GL_ONE_MINUS_DST_ALPHA: |
michael@0 | 434 | case LOCAL_GL_CONSTANT_COLOR: |
michael@0 | 435 | case LOCAL_GL_ONE_MINUS_CONSTANT_COLOR: |
michael@0 | 436 | case LOCAL_GL_CONSTANT_ALPHA: |
michael@0 | 437 | case LOCAL_GL_ONE_MINUS_CONSTANT_ALPHA: |
michael@0 | 438 | return true; |
michael@0 | 439 | default: |
michael@0 | 440 | ErrorInvalidEnumInfo(info, factor); |
michael@0 | 441 | return false; |
michael@0 | 442 | } |
michael@0 | 443 | } |
michael@0 | 444 | |
michael@0 | 445 | bool WebGLContext::ValidateBlendFuncSrcEnum(GLenum factor, const char *info) |
michael@0 | 446 | { |
michael@0 | 447 | if (factor == LOCAL_GL_SRC_ALPHA_SATURATE) |
michael@0 | 448 | return true; |
michael@0 | 449 | else |
michael@0 | 450 | return ValidateBlendFuncDstEnum(factor, info); |
michael@0 | 451 | } |
michael@0 | 452 | |
michael@0 | 453 | bool WebGLContext::ValidateBlendFuncEnumsCompatibility(GLenum sfactor, GLenum dfactor, const char *info) |
michael@0 | 454 | { |
michael@0 | 455 | bool sfactorIsConstantColor = sfactor == LOCAL_GL_CONSTANT_COLOR || |
michael@0 | 456 | sfactor == LOCAL_GL_ONE_MINUS_CONSTANT_COLOR; |
michael@0 | 457 | bool sfactorIsConstantAlpha = sfactor == LOCAL_GL_CONSTANT_ALPHA || |
michael@0 | 458 | sfactor == LOCAL_GL_ONE_MINUS_CONSTANT_ALPHA; |
michael@0 | 459 | bool dfactorIsConstantColor = dfactor == LOCAL_GL_CONSTANT_COLOR || |
michael@0 | 460 | dfactor == LOCAL_GL_ONE_MINUS_CONSTANT_COLOR; |
michael@0 | 461 | bool dfactorIsConstantAlpha = dfactor == LOCAL_GL_CONSTANT_ALPHA || |
michael@0 | 462 | dfactor == LOCAL_GL_ONE_MINUS_CONSTANT_ALPHA; |
michael@0 | 463 | if ( (sfactorIsConstantColor && dfactorIsConstantAlpha) || |
michael@0 | 464 | (dfactorIsConstantColor && sfactorIsConstantAlpha) ) { |
michael@0 | 465 | ErrorInvalidOperation("%s are mutually incompatible, see section 6.8 in the WebGL 1.0 spec", info); |
michael@0 | 466 | return false; |
michael@0 | 467 | } else { |
michael@0 | 468 | return true; |
michael@0 | 469 | } |
michael@0 | 470 | } |
michael@0 | 471 | |
michael@0 | 472 | bool WebGLContext::ValidateTextureTargetEnum(GLenum target, const char *info) |
michael@0 | 473 | { |
michael@0 | 474 | switch (target) { |
michael@0 | 475 | case LOCAL_GL_TEXTURE_2D: |
michael@0 | 476 | case LOCAL_GL_TEXTURE_CUBE_MAP: |
michael@0 | 477 | return true; |
michael@0 | 478 | default: |
michael@0 | 479 | ErrorInvalidEnumInfo(info, target); |
michael@0 | 480 | return false; |
michael@0 | 481 | } |
michael@0 | 482 | } |
michael@0 | 483 | |
michael@0 | 484 | bool WebGLContext::ValidateComparisonEnum(GLenum target, const char *info) |
michael@0 | 485 | { |
michael@0 | 486 | switch (target) { |
michael@0 | 487 | case LOCAL_GL_NEVER: |
michael@0 | 488 | case LOCAL_GL_LESS: |
michael@0 | 489 | case LOCAL_GL_LEQUAL: |
michael@0 | 490 | case LOCAL_GL_GREATER: |
michael@0 | 491 | case LOCAL_GL_GEQUAL: |
michael@0 | 492 | case LOCAL_GL_EQUAL: |
michael@0 | 493 | case LOCAL_GL_NOTEQUAL: |
michael@0 | 494 | case LOCAL_GL_ALWAYS: |
michael@0 | 495 | return true; |
michael@0 | 496 | default: |
michael@0 | 497 | ErrorInvalidEnumInfo(info, target); |
michael@0 | 498 | return false; |
michael@0 | 499 | } |
michael@0 | 500 | } |
michael@0 | 501 | |
michael@0 | 502 | bool WebGLContext::ValidateStencilOpEnum(GLenum action, const char *info) |
michael@0 | 503 | { |
michael@0 | 504 | switch (action) { |
michael@0 | 505 | case LOCAL_GL_KEEP: |
michael@0 | 506 | case LOCAL_GL_ZERO: |
michael@0 | 507 | case LOCAL_GL_REPLACE: |
michael@0 | 508 | case LOCAL_GL_INCR: |
michael@0 | 509 | case LOCAL_GL_INCR_WRAP: |
michael@0 | 510 | case LOCAL_GL_DECR: |
michael@0 | 511 | case LOCAL_GL_DECR_WRAP: |
michael@0 | 512 | case LOCAL_GL_INVERT: |
michael@0 | 513 | return true; |
michael@0 | 514 | default: |
michael@0 | 515 | ErrorInvalidEnumInfo(info, action); |
michael@0 | 516 | return false; |
michael@0 | 517 | } |
michael@0 | 518 | } |
michael@0 | 519 | |
michael@0 | 520 | bool WebGLContext::ValidateFaceEnum(GLenum face, const char *info) |
michael@0 | 521 | { |
michael@0 | 522 | switch (face) { |
michael@0 | 523 | case LOCAL_GL_FRONT: |
michael@0 | 524 | case LOCAL_GL_BACK: |
michael@0 | 525 | case LOCAL_GL_FRONT_AND_BACK: |
michael@0 | 526 | return true; |
michael@0 | 527 | default: |
michael@0 | 528 | ErrorInvalidEnumInfo(info, face); |
michael@0 | 529 | return false; |
michael@0 | 530 | } |
michael@0 | 531 | } |
michael@0 | 532 | |
michael@0 | 533 | bool WebGLContext::ValidateDrawModeEnum(GLenum mode, const char *info) |
michael@0 | 534 | { |
michael@0 | 535 | switch (mode) { |
michael@0 | 536 | case LOCAL_GL_TRIANGLES: |
michael@0 | 537 | case LOCAL_GL_TRIANGLE_STRIP: |
michael@0 | 538 | case LOCAL_GL_TRIANGLE_FAN: |
michael@0 | 539 | case LOCAL_GL_POINTS: |
michael@0 | 540 | case LOCAL_GL_LINE_STRIP: |
michael@0 | 541 | case LOCAL_GL_LINE_LOOP: |
michael@0 | 542 | case LOCAL_GL_LINES: |
michael@0 | 543 | return true; |
michael@0 | 544 | default: |
michael@0 | 545 | ErrorInvalidEnumInfo(info, mode); |
michael@0 | 546 | return false; |
michael@0 | 547 | } |
michael@0 | 548 | } |
michael@0 | 549 | |
michael@0 | 550 | bool WebGLContext::ValidateGLSLVariableName(const nsAString& name, const char *info) |
michael@0 | 551 | { |
michael@0 | 552 | if (name.IsEmpty()) |
michael@0 | 553 | return false; |
michael@0 | 554 | |
michael@0 | 555 | const uint32_t maxSize = 256; |
michael@0 | 556 | if (name.Length() > maxSize) { |
michael@0 | 557 | ErrorInvalidValue("%s: identifier is %d characters long, exceeds the maximum allowed length of %d characters", |
michael@0 | 558 | info, name.Length(), maxSize); |
michael@0 | 559 | return false; |
michael@0 | 560 | } |
michael@0 | 561 | |
michael@0 | 562 | if (!ValidateGLSLString(name, info)) { |
michael@0 | 563 | return false; |
michael@0 | 564 | } |
michael@0 | 565 | |
michael@0 | 566 | nsString prefix1 = NS_LITERAL_STRING("webgl_"); |
michael@0 | 567 | nsString prefix2 = NS_LITERAL_STRING("_webgl_"); |
michael@0 | 568 | |
michael@0 | 569 | if (Substring(name, 0, prefix1.Length()).Equals(prefix1) || |
michael@0 | 570 | Substring(name, 0, prefix2.Length()).Equals(prefix2)) |
michael@0 | 571 | { |
michael@0 | 572 | ErrorInvalidOperation("%s: string contains a reserved GLSL prefix", info); |
michael@0 | 573 | return false; |
michael@0 | 574 | } |
michael@0 | 575 | |
michael@0 | 576 | return true; |
michael@0 | 577 | } |
michael@0 | 578 | |
michael@0 | 579 | bool WebGLContext::ValidateGLSLString(const nsAString& string, const char *info) |
michael@0 | 580 | { |
michael@0 | 581 | for (uint32_t i = 0; i < string.Length(); ++i) { |
michael@0 | 582 | if (!ValidateGLSLCharacter(string.CharAt(i))) { |
michael@0 | 583 | ErrorInvalidValue("%s: string contains the illegal character '%d'", info, string.CharAt(i)); |
michael@0 | 584 | return false; |
michael@0 | 585 | } |
michael@0 | 586 | } |
michael@0 | 587 | |
michael@0 | 588 | return true; |
michael@0 | 589 | } |
michael@0 | 590 | |
michael@0 | 591 | /** |
michael@0 | 592 | * Return true if format is a valid texture image format for source, |
michael@0 | 593 | * taking into account enabled WebGL extensions. |
michael@0 | 594 | */ |
michael@0 | 595 | bool |
michael@0 | 596 | WebGLContext::ValidateTexImageFormat(GLenum format, WebGLTexImageFunc func) |
michael@0 | 597 | { |
michael@0 | 598 | /* Core WebGL texture formats */ |
michael@0 | 599 | if (format == LOCAL_GL_ALPHA || |
michael@0 | 600 | format == LOCAL_GL_RGB || |
michael@0 | 601 | format == LOCAL_GL_RGBA || |
michael@0 | 602 | format == LOCAL_GL_LUMINANCE || |
michael@0 | 603 | format == LOCAL_GL_LUMINANCE_ALPHA) |
michael@0 | 604 | { |
michael@0 | 605 | return true; |
michael@0 | 606 | } |
michael@0 | 607 | |
michael@0 | 608 | /* Only core formats are valid for CopyTex(Sub)?Image */ |
michael@0 | 609 | // TODO: Revisit this once color_buffer_(half_)?float lands |
michael@0 | 610 | if (IsCopyFunc(func)) { |
michael@0 | 611 | ErrorInvalidEnumWithName(this, "invalid format", format, func); |
michael@0 | 612 | return false; |
michael@0 | 613 | } |
michael@0 | 614 | |
michael@0 | 615 | /* WEBGL_depth_texture added formats */ |
michael@0 | 616 | if (format == LOCAL_GL_DEPTH_COMPONENT || |
michael@0 | 617 | format == LOCAL_GL_DEPTH_STENCIL) |
michael@0 | 618 | { |
michael@0 | 619 | bool validFormat = IsExtensionEnabled(WebGLExtensionID::WEBGL_depth_texture); |
michael@0 | 620 | if (!validFormat) |
michael@0 | 621 | ErrorInvalidEnum("%s: invalid format %s: need WEBGL_depth_texture enabled", |
michael@0 | 622 | InfoFrom(func), NameFrom(format)); |
michael@0 | 623 | return validFormat; |
michael@0 | 624 | } |
michael@0 | 625 | |
michael@0 | 626 | /* EXT_sRGB added formats */ |
michael@0 | 627 | if (format == LOCAL_GL_SRGB || |
michael@0 | 628 | format == LOCAL_GL_SRGB_ALPHA) |
michael@0 | 629 | { |
michael@0 | 630 | bool validFormat = IsExtensionEnabled(WebGLExtensionID::EXT_sRGB); |
michael@0 | 631 | if (!validFormat) |
michael@0 | 632 | ErrorInvalidEnum("%s: invalid format %s: need EXT_sRGB enabled", |
michael@0 | 633 | InfoFrom(func), NameFrom(format)); |
michael@0 | 634 | return validFormat; |
michael@0 | 635 | } |
michael@0 | 636 | |
michael@0 | 637 | /* WEBGL_compressed_texture_atc added formats */ |
michael@0 | 638 | if (format == LOCAL_GL_ATC_RGB || |
michael@0 | 639 | format == LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA || |
michael@0 | 640 | format == LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA) |
michael@0 | 641 | { |
michael@0 | 642 | bool validFormat = IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_atc); |
michael@0 | 643 | if (!validFormat) |
michael@0 | 644 | ErrorInvalidEnum("%s: invalid format %s: need WEBGL_compressed_texture_atc enabled", |
michael@0 | 645 | InfoFrom(func), NameFrom(format)); |
michael@0 | 646 | return validFormat; |
michael@0 | 647 | } |
michael@0 | 648 | |
michael@0 | 649 | // WEBGL_compressed_texture_etc1 |
michael@0 | 650 | if (format == LOCAL_GL_ETC1_RGB8_OES) { |
michael@0 | 651 | bool validFormat = IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_etc1); |
michael@0 | 652 | if (!validFormat) |
michael@0 | 653 | ErrorInvalidEnum("%s: invalid format %s: need WEBGL_compressed_texture_etc1 enabled", |
michael@0 | 654 | InfoFrom(func), NameFrom(format)); |
michael@0 | 655 | return validFormat; |
michael@0 | 656 | } |
michael@0 | 657 | |
michael@0 | 658 | |
michael@0 | 659 | if (format == LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1 || |
michael@0 | 660 | format == LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1 || |
michael@0 | 661 | format == LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1 || |
michael@0 | 662 | format == LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1) |
michael@0 | 663 | { |
michael@0 | 664 | bool validFormat = IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_pvrtc); |
michael@0 | 665 | if (!validFormat) |
michael@0 | 666 | ErrorInvalidEnum("%s: invalid format %s: need WEBGL_compressed_texture_pvrtc enabled", |
michael@0 | 667 | InfoFrom(func), NameFrom(format)); |
michael@0 | 668 | return validFormat; |
michael@0 | 669 | } |
michael@0 | 670 | |
michael@0 | 671 | |
michael@0 | 672 | if (format == LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT || |
michael@0 | 673 | format == LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT || |
michael@0 | 674 | format == LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT || |
michael@0 | 675 | format == LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT) |
michael@0 | 676 | { |
michael@0 | 677 | bool validFormat = IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_s3tc); |
michael@0 | 678 | if (!validFormat) |
michael@0 | 679 | ErrorInvalidEnum("%s: invalid format %s: need WEBGL_compressed_texture_s3tc enabled", |
michael@0 | 680 | InfoFrom(func), NameFrom(format)); |
michael@0 | 681 | return validFormat; |
michael@0 | 682 | } |
michael@0 | 683 | |
michael@0 | 684 | ErrorInvalidEnumWithName(this, "invalid format", format, func); |
michael@0 | 685 | |
michael@0 | 686 | return false; |
michael@0 | 687 | } |
michael@0 | 688 | |
michael@0 | 689 | /** |
michael@0 | 690 | * Check if the given texture target is valid for TexImage. |
michael@0 | 691 | */ |
michael@0 | 692 | bool |
michael@0 | 693 | WebGLContext::ValidateTexImageTarget(GLuint dims, GLenum target, WebGLTexImageFunc func) |
michael@0 | 694 | { |
michael@0 | 695 | switch (dims) { |
michael@0 | 696 | case 2: |
michael@0 | 697 | if (target == LOCAL_GL_TEXTURE_2D || |
michael@0 | 698 | IsTexImageCubemapTarget(target)) |
michael@0 | 699 | { |
michael@0 | 700 | return true; |
michael@0 | 701 | } |
michael@0 | 702 | |
michael@0 | 703 | ErrorInvalidEnumWithName(this, "invalid target", target, func); |
michael@0 | 704 | return false; |
michael@0 | 705 | |
michael@0 | 706 | default: |
michael@0 | 707 | MOZ_ASSERT(false, "ValidateTexImageTarget: Invalid dims"); |
michael@0 | 708 | } |
michael@0 | 709 | |
michael@0 | 710 | return false; |
michael@0 | 711 | } |
michael@0 | 712 | |
michael@0 | 713 | /** |
michael@0 | 714 | * Return true if type is a valid texture image type for source, |
michael@0 | 715 | * taking into account enabled WebGL extensions. |
michael@0 | 716 | */ |
michael@0 | 717 | bool |
michael@0 | 718 | WebGLContext::ValidateTexImageType(GLenum type, WebGLTexImageFunc func) |
michael@0 | 719 | { |
michael@0 | 720 | /* Core WebGL texture types */ |
michael@0 | 721 | if (type == LOCAL_GL_UNSIGNED_BYTE || |
michael@0 | 722 | type == LOCAL_GL_UNSIGNED_SHORT_5_6_5 || |
michael@0 | 723 | type == LOCAL_GL_UNSIGNED_SHORT_4_4_4_4 || |
michael@0 | 724 | type == LOCAL_GL_UNSIGNED_SHORT_5_5_5_1) |
michael@0 | 725 | { |
michael@0 | 726 | return true; |
michael@0 | 727 | } |
michael@0 | 728 | |
michael@0 | 729 | /* OES_texture_float added types */ |
michael@0 | 730 | if (type == LOCAL_GL_FLOAT) { |
michael@0 | 731 | bool validType = IsExtensionEnabled(WebGLExtensionID::OES_texture_float); |
michael@0 | 732 | if (!validType) |
michael@0 | 733 | ErrorInvalidEnum("%s: invalid type %s: need OES_texture_float enabled", |
michael@0 | 734 | InfoFrom(func), NameFrom(type)); |
michael@0 | 735 | return validType; |
michael@0 | 736 | } |
michael@0 | 737 | |
michael@0 | 738 | /* OES_texture_half_float add types */ |
michael@0 | 739 | if (type == LOCAL_GL_HALF_FLOAT_OES) { |
michael@0 | 740 | bool validType = IsExtensionEnabled(WebGLExtensionID::OES_texture_half_float); |
michael@0 | 741 | if (!validType) |
michael@0 | 742 | ErrorInvalidEnum("%s: invalid type %s: need OES_texture_half_float enabled", |
michael@0 | 743 | InfoFrom(func), NameFrom(type)); |
michael@0 | 744 | return validType; |
michael@0 | 745 | } |
michael@0 | 746 | |
michael@0 | 747 | /* WEBGL_depth_texture added types */ |
michael@0 | 748 | if (type == LOCAL_GL_UNSIGNED_SHORT || |
michael@0 | 749 | type == LOCAL_GL_UNSIGNED_INT || |
michael@0 | 750 | type == LOCAL_GL_UNSIGNED_INT_24_8) |
michael@0 | 751 | { |
michael@0 | 752 | bool validType = IsExtensionEnabled(WebGLExtensionID::WEBGL_depth_texture); |
michael@0 | 753 | if (!validType) |
michael@0 | 754 | ErrorInvalidEnum("%s: invalid type %s: need WEBGL_depth_texture enabled", |
michael@0 | 755 | InfoFrom(func), NameFrom(type)); |
michael@0 | 756 | return validType; |
michael@0 | 757 | } |
michael@0 | 758 | |
michael@0 | 759 | ErrorInvalidEnumWithName(this, "invalid type", type, func); |
michael@0 | 760 | return false; |
michael@0 | 761 | } |
michael@0 | 762 | |
michael@0 | 763 | /** |
michael@0 | 764 | * Validate texture image sizing extra constraints for |
michael@0 | 765 | * CompressedTex(Sub)?Image. |
michael@0 | 766 | */ |
michael@0 | 767 | // TODO: WebGL 2 |
michael@0 | 768 | bool |
michael@0 | 769 | WebGLContext::ValidateCompTexImageSize(GLenum target, GLint level, GLenum format, |
michael@0 | 770 | GLint xoffset, GLint yoffset, |
michael@0 | 771 | GLsizei width, GLsizei height, |
michael@0 | 772 | GLsizei levelWidth, GLsizei levelHeight, |
michael@0 | 773 | WebGLTexImageFunc func) |
michael@0 | 774 | { |
michael@0 | 775 | // Negative parameters must already have been handled above |
michael@0 | 776 | MOZ_ASSERT(xoffset >= 0 && yoffset >= 0 && |
michael@0 | 777 | width >= 0 && height >= 0); |
michael@0 | 778 | |
michael@0 | 779 | if (xoffset + width > (GLint) levelWidth) { |
michael@0 | 780 | ErrorInvalidValue("%s: xoffset + width must be <= levelWidth", InfoFrom(func)); |
michael@0 | 781 | return false; |
michael@0 | 782 | } |
michael@0 | 783 | |
michael@0 | 784 | if (yoffset + height > (GLint) levelHeight) { |
michael@0 | 785 | ErrorInvalidValue("%s: yoffset + height must be <= levelHeight", InfoFrom(func)); |
michael@0 | 786 | return false; |
michael@0 | 787 | } |
michael@0 | 788 | |
michael@0 | 789 | GLint blockWidth = 1; |
michael@0 | 790 | GLint blockHeight = 1; |
michael@0 | 791 | BlockSizeFor(format, &blockWidth, &blockHeight); |
michael@0 | 792 | |
michael@0 | 793 | /* If blockWidth || blockHeight != 1, then the compressed format |
michael@0 | 794 | * had block-based constraints to be checked. (For example, PVRTC is compressed but |
michael@0 | 795 | * isn't a block-based format) |
michael@0 | 796 | */ |
michael@0 | 797 | if (blockWidth != 1 || blockHeight != 1) { |
michael@0 | 798 | /* offsets must be multiple of block size */ |
michael@0 | 799 | if (xoffset % blockWidth != 0) { |
michael@0 | 800 | ErrorInvalidOperation("%s: xoffset must be multiple of %d", |
michael@0 | 801 | InfoFrom(func), blockWidth); |
michael@0 | 802 | return false; |
michael@0 | 803 | } |
michael@0 | 804 | |
michael@0 | 805 | if (yoffset % blockHeight != 0) { |
michael@0 | 806 | ErrorInvalidOperation("%s: yoffset must be multiple of %d", |
michael@0 | 807 | InfoFrom(func), blockHeight); |
michael@0 | 808 | return false; |
michael@0 | 809 | } |
michael@0 | 810 | |
michael@0 | 811 | /* The size must be a multiple of blockWidth and blockHeight, |
michael@0 | 812 | * or must be using offset+size that exactly hits the edge. |
michael@0 | 813 | * Important for small mipmap levels. |
michael@0 | 814 | */ |
michael@0 | 815 | /* https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/ |
michael@0 | 816 | * "When level equals zero width and height must be a multiple of 4. When |
michael@0 | 817 | * level is greater than 0 width and height must be 0, 1, 2 or a multiple of 4. |
michael@0 | 818 | * If they are not an INVALID_OPERATION error is generated." |
michael@0 | 819 | */ |
michael@0 | 820 | if (level == 0) { |
michael@0 | 821 | if (width % blockWidth != 0) { |
michael@0 | 822 | ErrorInvalidOperation("%s: width of level 0 must be multple of %d", |
michael@0 | 823 | InfoFrom(func), blockWidth); |
michael@0 | 824 | return false; |
michael@0 | 825 | } |
michael@0 | 826 | |
michael@0 | 827 | if (height % blockHeight != 0) { |
michael@0 | 828 | ErrorInvalidOperation("%s: height of level 0 must be multipel of %d", |
michael@0 | 829 | InfoFrom(func), blockHeight); |
michael@0 | 830 | return false; |
michael@0 | 831 | } |
michael@0 | 832 | } |
michael@0 | 833 | else if (level > 0) { |
michael@0 | 834 | if (width % blockWidth != 0 && width > 2) { |
michael@0 | 835 | ErrorInvalidOperation("%s: width of level %d must be multiple" |
michael@0 | 836 | " of %d or 0, 1, 2", |
michael@0 | 837 | InfoFrom(func), level, blockWidth); |
michael@0 | 838 | return false; |
michael@0 | 839 | } |
michael@0 | 840 | |
michael@0 | 841 | if (height % blockHeight != 0 && height > 2) { |
michael@0 | 842 | ErrorInvalidOperation("%s: height of level %d must be multiple" |
michael@0 | 843 | " of %d or 0, 1, 2", |
michael@0 | 844 | InfoFrom(func), level, blockHeight); |
michael@0 | 845 | return false; |
michael@0 | 846 | } |
michael@0 | 847 | } |
michael@0 | 848 | |
michael@0 | 849 | if (IsSubFunc(func)) { |
michael@0 | 850 | if ((xoffset % blockWidth) != 0) { |
michael@0 | 851 | ErrorInvalidOperation("%s: xoffset must be multiple of %d", |
michael@0 | 852 | InfoFrom(func), blockWidth); |
michael@0 | 853 | return false; |
michael@0 | 854 | } |
michael@0 | 855 | |
michael@0 | 856 | if (yoffset % blockHeight != 0) { |
michael@0 | 857 | ErrorInvalidOperation("%s: yoffset must be multiple of %d", |
michael@0 | 858 | InfoFrom(func), blockHeight); |
michael@0 | 859 | return false; |
michael@0 | 860 | } |
michael@0 | 861 | } |
michael@0 | 862 | } |
michael@0 | 863 | |
michael@0 | 864 | switch (format) { |
michael@0 | 865 | case LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1: |
michael@0 | 866 | case LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1: |
michael@0 | 867 | case LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1: |
michael@0 | 868 | case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1: |
michael@0 | 869 | if (!is_pot_assuming_nonnegative(width) || |
michael@0 | 870 | !is_pot_assuming_nonnegative(height)) |
michael@0 | 871 | { |
michael@0 | 872 | ErrorInvalidValue("%s: width and height must be powers of two", |
michael@0 | 873 | InfoFrom(func)); |
michael@0 | 874 | return false; |
michael@0 | 875 | } |
michael@0 | 876 | } |
michael@0 | 877 | |
michael@0 | 878 | return true; |
michael@0 | 879 | } |
michael@0 | 880 | |
michael@0 | 881 | /** |
michael@0 | 882 | * Return true if the enough data is present to satisfy compressed |
michael@0 | 883 | * texture format constraints. |
michael@0 | 884 | */ |
michael@0 | 885 | bool |
michael@0 | 886 | WebGLContext::ValidateCompTexImageDataSize(GLint level, GLenum format, |
michael@0 | 887 | GLsizei width, GLsizei height, |
michael@0 | 888 | uint32_t byteLength, WebGLTexImageFunc func) |
michael@0 | 889 | { |
michael@0 | 890 | // negative width and height must already have been handled above |
michael@0 | 891 | MOZ_ASSERT(width >= 0 && height >= 0); |
michael@0 | 892 | |
michael@0 | 893 | CheckedUint32 required_byteLength = 0; |
michael@0 | 894 | |
michael@0 | 895 | switch (format) { |
michael@0 | 896 | case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT: |
michael@0 | 897 | case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: |
michael@0 | 898 | case LOCAL_GL_ATC_RGB: |
michael@0 | 899 | case LOCAL_GL_ETC1_RGB8_OES: |
michael@0 | 900 | { |
michael@0 | 901 | required_byteLength = ((CheckedUint32(width) + 3) / 4) * ((CheckedUint32(height) + 3) / 4) * 8; |
michael@0 | 902 | break; |
michael@0 | 903 | } |
michael@0 | 904 | case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: |
michael@0 | 905 | case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: |
michael@0 | 906 | case LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA: |
michael@0 | 907 | case LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA: |
michael@0 | 908 | { |
michael@0 | 909 | required_byteLength = ((CheckedUint32(width) + 3) / 4) * ((CheckedUint32(height) + 3) / 4) * 16; |
michael@0 | 910 | break; |
michael@0 | 911 | } |
michael@0 | 912 | case LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1: |
michael@0 | 913 | case LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1: |
michael@0 | 914 | { |
michael@0 | 915 | required_byteLength = CheckedUint32(std::max(width, 8)) * CheckedUint32(std::max(height, 8)) / 2; |
michael@0 | 916 | break; |
michael@0 | 917 | } |
michael@0 | 918 | case LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1: |
michael@0 | 919 | case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1: |
michael@0 | 920 | { |
michael@0 | 921 | required_byteLength = CheckedUint32(std::max(width, 16)) * CheckedUint32(std::max(height, 8)) / 4; |
michael@0 | 922 | break; |
michael@0 | 923 | } |
michael@0 | 924 | } |
michael@0 | 925 | |
michael@0 | 926 | if (!required_byteLength.isValid() || required_byteLength.value() != byteLength) { |
michael@0 | 927 | ErrorInvalidValue("%s: data size does not match dimensions", InfoFrom(func)); |
michael@0 | 928 | return false; |
michael@0 | 929 | } |
michael@0 | 930 | |
michael@0 | 931 | return true; |
michael@0 | 932 | } |
michael@0 | 933 | |
michael@0 | 934 | /** |
michael@0 | 935 | * Validate the width, height, and depth of a texture image, \return |
michael@0 | 936 | * true is valid, false otherwise. |
michael@0 | 937 | * Used by all the (Compressed|Copy)?Tex(Sub)?Image functions. |
michael@0 | 938 | * Target and level must have been validated before calling. |
michael@0 | 939 | */ |
michael@0 | 940 | bool |
michael@0 | 941 | WebGLContext::ValidateTexImageSize(GLenum target, GLint level, |
michael@0 | 942 | GLint width, GLint height, GLint depth, |
michael@0 | 943 | WebGLTexImageFunc func) |
michael@0 | 944 | { |
michael@0 | 945 | MOZ_ASSERT(level >= 0, "level should already be validated"); |
michael@0 | 946 | |
michael@0 | 947 | /* Bug 966630: maxTextureSize >> level runs into "undefined" |
michael@0 | 948 | * behaviour depending on ISA. For example, on Intel shifts |
michael@0 | 949 | * amounts are mod 64 (in 64-bit mode on 64-bit dest) and mod 32 |
michael@0 | 950 | * otherwise. This means 16384 >> 0x10000001 == 8192 which isn't |
michael@0 | 951 | * what would be expected. Make the required behaviour explicit by |
michael@0 | 952 | * clamping to a shift of 31 bits if level is greater than that |
michael@0 | 953 | * ammount. This will give 0 that if (!maxAllowedSize) is |
michael@0 | 954 | * expecting. |
michael@0 | 955 | */ |
michael@0 | 956 | |
michael@0 | 957 | if (level > 31) |
michael@0 | 958 | level = 31; |
michael@0 | 959 | |
michael@0 | 960 | const GLuint maxTexImageSize = MaxTextureSizeForTarget(target) >> level; |
michael@0 | 961 | const bool isCubemapTarget = IsTexImageCubemapTarget(target); |
michael@0 | 962 | const bool isSub = IsSubFunc(func); |
michael@0 | 963 | |
michael@0 | 964 | if (!isSub && isCubemapTarget && (width != height)) { |
michael@0 | 965 | /* GL ES Version 2.0.25 - 3.7.1 Texture Image Specification |
michael@0 | 966 | * "When the target parameter to TexImage2D is one of the |
michael@0 | 967 | * six cube map two-dimensional image targets, the error |
michael@0 | 968 | * INVALID_VALUE is generated if the width and height |
michael@0 | 969 | * parameters are not equal." |
michael@0 | 970 | */ |
michael@0 | 971 | ErrorInvalidValue("%s: for cube map, width must equal height", InfoFrom(func)); |
michael@0 | 972 | return false; |
michael@0 | 973 | } |
michael@0 | 974 | |
michael@0 | 975 | if (target == LOCAL_GL_TEXTURE_2D || isCubemapTarget) |
michael@0 | 976 | { |
michael@0 | 977 | /* GL ES Version 2.0.25 - 3.7.1 Texture Image Specification |
michael@0 | 978 | * "If wt and ht are the specified image width and height, |
michael@0 | 979 | * and if either wt or ht are less than zero, then the error |
michael@0 | 980 | * INVALID_VALUE is generated." |
michael@0 | 981 | */ |
michael@0 | 982 | if (width < 0) { |
michael@0 | 983 | ErrorInvalidValue("%s: width must be >= 0", InfoFrom(func)); |
michael@0 | 984 | return false; |
michael@0 | 985 | } |
michael@0 | 986 | |
michael@0 | 987 | if (height < 0) { |
michael@0 | 988 | ErrorInvalidValue("%s: height must be >= 0", InfoFrom(func)); |
michael@0 | 989 | return false; |
michael@0 | 990 | } |
michael@0 | 991 | |
michael@0 | 992 | /* GL ES Version 2.0.25 - 3.7.1 Texture Image Specification |
michael@0 | 993 | * "The maximum allowable width and height of a |
michael@0 | 994 | * two-dimensional texture image must be at least 2**(k−lod) |
michael@0 | 995 | * for image arrays of level zero through k, where k is the |
michael@0 | 996 | * log base 2 of MAX_TEXTURE_SIZE. and lod is the |
michael@0 | 997 | * level-of-detail of the image array. It may be zero for |
michael@0 | 998 | * image arrays of any level-of-detail greater than k. The |
michael@0 | 999 | * error INVALID_VALUE is generated if the specified image |
michael@0 | 1000 | * is too large to be stored under any conditions. |
michael@0 | 1001 | */ |
michael@0 | 1002 | if (width > (int) maxTexImageSize) { |
michael@0 | 1003 | ErrorInvalidValue("%s: the maximum width for level %d is %u", |
michael@0 | 1004 | InfoFrom(func), level, maxTexImageSize); |
michael@0 | 1005 | return false; |
michael@0 | 1006 | } |
michael@0 | 1007 | |
michael@0 | 1008 | if (height > (int) maxTexImageSize) { |
michael@0 | 1009 | ErrorInvalidValue("%s: tex maximum height for level %d is %u", |
michael@0 | 1010 | InfoFrom(func), level, maxTexImageSize); |
michael@0 | 1011 | return false; |
michael@0 | 1012 | } |
michael@0 | 1013 | |
michael@0 | 1014 | /* GL ES Version 2.0.25 - 3.7.1 Texture Image Specification |
michael@0 | 1015 | * "If level is greater than zero, and either width or |
michael@0 | 1016 | * height is not a power-of-two, the error INVALID_VALUE is |
michael@0 | 1017 | * generated." |
michael@0 | 1018 | */ |
michael@0 | 1019 | if (level > 0) { |
michael@0 | 1020 | if (!is_pot_assuming_nonnegative(width)) { |
michael@0 | 1021 | ErrorInvalidValue("%s: level >= 0, width of %d must be a power of two.", |
michael@0 | 1022 | InfoFrom(func), width); |
michael@0 | 1023 | return false; |
michael@0 | 1024 | } |
michael@0 | 1025 | |
michael@0 | 1026 | if (!is_pot_assuming_nonnegative(height)) { |
michael@0 | 1027 | ErrorInvalidValue("%s: level >= 0, height of %d must be a power of two.", |
michael@0 | 1028 | InfoFrom(func), height); |
michael@0 | 1029 | return false; |
michael@0 | 1030 | } |
michael@0 | 1031 | } |
michael@0 | 1032 | } |
michael@0 | 1033 | |
michael@0 | 1034 | // TODO: WebGL 2 |
michael@0 | 1035 | if (target == LOCAL_GL_TEXTURE_3D) { |
michael@0 | 1036 | if (depth < 0) { |
michael@0 | 1037 | ErrorInvalidValue("%s: depth must be >= 0", InfoFrom(func)); |
michael@0 | 1038 | return false; |
michael@0 | 1039 | } |
michael@0 | 1040 | |
michael@0 | 1041 | if (!is_pot_assuming_nonnegative(depth)) { |
michael@0 | 1042 | ErrorInvalidValue("%s: level >= 0, depth of %d must be a power of two.", |
michael@0 | 1043 | InfoFrom(func), depth); |
michael@0 | 1044 | return false; |
michael@0 | 1045 | } |
michael@0 | 1046 | } |
michael@0 | 1047 | |
michael@0 | 1048 | return true; |
michael@0 | 1049 | } |
michael@0 | 1050 | |
michael@0 | 1051 | /** |
michael@0 | 1052 | * Validate texture image sizing for Tex(Sub)?Image variants. |
michael@0 | 1053 | */ |
michael@0 | 1054 | // TODO: WebGL 2. Update this to handle 3D textures. |
michael@0 | 1055 | bool |
michael@0 | 1056 | WebGLContext::ValidateTexSubImageSize(GLint xoffset, GLint yoffset, GLint /*zoffset*/, |
michael@0 | 1057 | GLsizei width, GLsizei height, GLsizei /*depth*/, |
michael@0 | 1058 | GLsizei baseWidth, GLsizei baseHeight, GLsizei /*baseDepth*/, |
michael@0 | 1059 | WebGLTexImageFunc func) |
michael@0 | 1060 | { |
michael@0 | 1061 | /* GL ES Version 2.0.25 - 3.7.1 Texture Image Specification |
michael@0 | 1062 | * "Taking wt and ht to be the specified width and height of the |
michael@0 | 1063 | * texture array, and taking x, y, w, and h to be the xoffset, |
michael@0 | 1064 | * yoffset, width, and height argument values, any of the |
michael@0 | 1065 | * following relationships generates the error INVALID_VALUE: |
michael@0 | 1066 | * x < 0 |
michael@0 | 1067 | * x + w > wt |
michael@0 | 1068 | * y < 0 |
michael@0 | 1069 | * y + h > ht" |
michael@0 | 1070 | */ |
michael@0 | 1071 | |
michael@0 | 1072 | if (xoffset < 0) { |
michael@0 | 1073 | ErrorInvalidValue("%s: xoffset must be >= 0", InfoFrom(func)); |
michael@0 | 1074 | return false; |
michael@0 | 1075 | } |
michael@0 | 1076 | |
michael@0 | 1077 | if (yoffset < 0) { |
michael@0 | 1078 | ErrorInvalidValue("%s: yoffset must be >= 0", InfoFrom(func)); |
michael@0 | 1079 | return false; |
michael@0 | 1080 | } |
michael@0 | 1081 | |
michael@0 | 1082 | if (!CanvasUtils::CheckSaneSubrectSize(xoffset, yoffset, width, height, baseWidth, baseHeight)) { |
michael@0 | 1083 | ErrorInvalidValue("%s: subtexture rectangle out-of-bounds", InfoFrom(func)); |
michael@0 | 1084 | return false; |
michael@0 | 1085 | } |
michael@0 | 1086 | |
michael@0 | 1087 | return true; |
michael@0 | 1088 | } |
michael@0 | 1089 | |
michael@0 | 1090 | /** |
michael@0 | 1091 | * Return the bits per texel for format & type combination. |
michael@0 | 1092 | * Assumes that format & type are a valid combination as checked with |
michael@0 | 1093 | * ValidateTexImageFormatAndType(). |
michael@0 | 1094 | */ |
michael@0 | 1095 | uint32_t |
michael@0 | 1096 | WebGLContext::GetBitsPerTexel(GLenum format, GLenum type) |
michael@0 | 1097 | { |
michael@0 | 1098 | // If there is no defined format or type, we're not taking up any memory |
michael@0 | 1099 | if (!format || !type) { |
michael@0 | 1100 | return 0; |
michael@0 | 1101 | } |
michael@0 | 1102 | |
michael@0 | 1103 | /* Known fixed-sized types */ |
michael@0 | 1104 | if (type == LOCAL_GL_UNSIGNED_SHORT_4_4_4_4 || |
michael@0 | 1105 | type == LOCAL_GL_UNSIGNED_SHORT_5_5_5_1 || |
michael@0 | 1106 | type == LOCAL_GL_UNSIGNED_SHORT_5_6_5) |
michael@0 | 1107 | { |
michael@0 | 1108 | return 16; |
michael@0 | 1109 | } |
michael@0 | 1110 | |
michael@0 | 1111 | if (type == LOCAL_GL_UNSIGNED_INT_24_8) |
michael@0 | 1112 | return 32; |
michael@0 | 1113 | |
michael@0 | 1114 | int bitsPerComponent = 0; |
michael@0 | 1115 | switch (type) { |
michael@0 | 1116 | case LOCAL_GL_UNSIGNED_BYTE: |
michael@0 | 1117 | bitsPerComponent = 8; |
michael@0 | 1118 | break; |
michael@0 | 1119 | |
michael@0 | 1120 | case LOCAL_GL_HALF_FLOAT: |
michael@0 | 1121 | case LOCAL_GL_HALF_FLOAT_OES: |
michael@0 | 1122 | case LOCAL_GL_UNSIGNED_SHORT: |
michael@0 | 1123 | bitsPerComponent = 16; |
michael@0 | 1124 | break; |
michael@0 | 1125 | |
michael@0 | 1126 | case LOCAL_GL_FLOAT: |
michael@0 | 1127 | case LOCAL_GL_UNSIGNED_INT: |
michael@0 | 1128 | bitsPerComponent = 32; |
michael@0 | 1129 | break; |
michael@0 | 1130 | |
michael@0 | 1131 | default: |
michael@0 | 1132 | MOZ_ASSERT(false, "Unhandled type."); |
michael@0 | 1133 | break; |
michael@0 | 1134 | } |
michael@0 | 1135 | |
michael@0 | 1136 | switch (format) { |
michael@0 | 1137 | // Uncompressed formats |
michael@0 | 1138 | case LOCAL_GL_ALPHA: |
michael@0 | 1139 | case LOCAL_GL_LUMINANCE: |
michael@0 | 1140 | case LOCAL_GL_DEPTH_COMPONENT: |
michael@0 | 1141 | case LOCAL_GL_DEPTH_STENCIL: |
michael@0 | 1142 | return 1 * bitsPerComponent; |
michael@0 | 1143 | |
michael@0 | 1144 | case LOCAL_GL_LUMINANCE_ALPHA: |
michael@0 | 1145 | return 2 * bitsPerComponent; |
michael@0 | 1146 | |
michael@0 | 1147 | case LOCAL_GL_RGB: |
michael@0 | 1148 | case LOCAL_GL_RGB32F: |
michael@0 | 1149 | case LOCAL_GL_SRGB_EXT: |
michael@0 | 1150 | return 3 * bitsPerComponent; |
michael@0 | 1151 | |
michael@0 | 1152 | case LOCAL_GL_RGBA: |
michael@0 | 1153 | case LOCAL_GL_RGBA32F: |
michael@0 | 1154 | case LOCAL_GL_SRGB_ALPHA_EXT: |
michael@0 | 1155 | return 4 * bitsPerComponent; |
michael@0 | 1156 | |
michael@0 | 1157 | // Compressed formats |
michael@0 | 1158 | case LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1: |
michael@0 | 1159 | case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1: |
michael@0 | 1160 | return 2; |
michael@0 | 1161 | |
michael@0 | 1162 | case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT: |
michael@0 | 1163 | case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: |
michael@0 | 1164 | case LOCAL_GL_ATC_RGB: |
michael@0 | 1165 | case LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1: |
michael@0 | 1166 | case LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1: |
michael@0 | 1167 | case LOCAL_GL_ETC1_RGB8_OES: |
michael@0 | 1168 | return 4; |
michael@0 | 1169 | |
michael@0 | 1170 | case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: |
michael@0 | 1171 | case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: |
michael@0 | 1172 | case LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA: |
michael@0 | 1173 | case LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA: |
michael@0 | 1174 | return 8; |
michael@0 | 1175 | |
michael@0 | 1176 | default: |
michael@0 | 1177 | break; |
michael@0 | 1178 | } |
michael@0 | 1179 | |
michael@0 | 1180 | MOZ_ASSERT(false, "Unhandled format+type combo."); |
michael@0 | 1181 | return 0; |
michael@0 | 1182 | } |
michael@0 | 1183 | |
michael@0 | 1184 | /** |
michael@0 | 1185 | * Perform validation of format/type combinations for TexImage variants. |
michael@0 | 1186 | * Returns true if the format/type is a valid combination, false otherwise. |
michael@0 | 1187 | */ |
michael@0 | 1188 | bool |
michael@0 | 1189 | WebGLContext::ValidateTexImageFormatAndType(GLenum format, GLenum type, WebGLTexImageFunc func) |
michael@0 | 1190 | { |
michael@0 | 1191 | if (!ValidateTexImageFormat(format, func) || |
michael@0 | 1192 | !ValidateTexImageType(type, func)) |
michael@0 | 1193 | { |
michael@0 | 1194 | return false; |
michael@0 | 1195 | } |
michael@0 | 1196 | |
michael@0 | 1197 | bool validCombo = false; |
michael@0 | 1198 | |
michael@0 | 1199 | switch (format) { |
michael@0 | 1200 | case LOCAL_GL_ALPHA: |
michael@0 | 1201 | case LOCAL_GL_LUMINANCE: |
michael@0 | 1202 | case LOCAL_GL_LUMINANCE_ALPHA: |
michael@0 | 1203 | validCombo = (type == LOCAL_GL_UNSIGNED_BYTE || |
michael@0 | 1204 | type == LOCAL_GL_HALF_FLOAT || |
michael@0 | 1205 | type == LOCAL_GL_HALF_FLOAT_OES || |
michael@0 | 1206 | type == LOCAL_GL_FLOAT); |
michael@0 | 1207 | break; |
michael@0 | 1208 | |
michael@0 | 1209 | case LOCAL_GL_RGB: |
michael@0 | 1210 | case LOCAL_GL_SRGB: |
michael@0 | 1211 | validCombo = (type == LOCAL_GL_UNSIGNED_BYTE || |
michael@0 | 1212 | type == LOCAL_GL_UNSIGNED_SHORT_5_6_5 || |
michael@0 | 1213 | type == LOCAL_GL_HALF_FLOAT || |
michael@0 | 1214 | type == LOCAL_GL_HALF_FLOAT_OES || |
michael@0 | 1215 | type == LOCAL_GL_FLOAT); |
michael@0 | 1216 | break; |
michael@0 | 1217 | |
michael@0 | 1218 | case LOCAL_GL_RGBA: |
michael@0 | 1219 | case LOCAL_GL_SRGB_ALPHA: |
michael@0 | 1220 | validCombo = (type == LOCAL_GL_UNSIGNED_BYTE || |
michael@0 | 1221 | type == LOCAL_GL_UNSIGNED_SHORT_4_4_4_4 || |
michael@0 | 1222 | type == LOCAL_GL_UNSIGNED_SHORT_5_5_5_1 || |
michael@0 | 1223 | type == LOCAL_GL_HALF_FLOAT || |
michael@0 | 1224 | type == LOCAL_GL_HALF_FLOAT_OES || |
michael@0 | 1225 | type == LOCAL_GL_FLOAT); |
michael@0 | 1226 | break; |
michael@0 | 1227 | |
michael@0 | 1228 | case LOCAL_GL_DEPTH_COMPONENT: |
michael@0 | 1229 | validCombo = (type == LOCAL_GL_UNSIGNED_SHORT || |
michael@0 | 1230 | type == LOCAL_GL_UNSIGNED_INT); |
michael@0 | 1231 | break; |
michael@0 | 1232 | |
michael@0 | 1233 | case LOCAL_GL_DEPTH_STENCIL: |
michael@0 | 1234 | validCombo = (type == LOCAL_GL_UNSIGNED_INT_24_8); |
michael@0 | 1235 | break; |
michael@0 | 1236 | |
michael@0 | 1237 | case LOCAL_GL_ATC_RGB: |
michael@0 | 1238 | case LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA: |
michael@0 | 1239 | case LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA: |
michael@0 | 1240 | case LOCAL_GL_ETC1_RGB8_OES: |
michael@0 | 1241 | case LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1: |
michael@0 | 1242 | case LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1: |
michael@0 | 1243 | case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1: |
michael@0 | 1244 | case LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1: |
michael@0 | 1245 | case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT: |
michael@0 | 1246 | case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: |
michael@0 | 1247 | case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: |
michael@0 | 1248 | case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: |
michael@0 | 1249 | validCombo = (type == LOCAL_GL_UNSIGNED_BYTE); |
michael@0 | 1250 | break; |
michael@0 | 1251 | |
michael@0 | 1252 | default: |
michael@0 | 1253 | // Only valid formats should be passed to the switch stmt. |
michael@0 | 1254 | MOZ_ASSERT(false, "Unexpected format and type combo. How'd this happen?"); |
michael@0 | 1255 | validCombo = false; |
michael@0 | 1256 | // Fall through to return an InvalidOperations. This will alert us to the |
michael@0 | 1257 | // unexpected case that needs fixing in builds without asserts. |
michael@0 | 1258 | } |
michael@0 | 1259 | |
michael@0 | 1260 | if (!validCombo) |
michael@0 | 1261 | ErrorInvalidOperation("%s: invalid combination of format %s and type %s", |
michael@0 | 1262 | InfoFrom(func), NameFrom(format), NameFrom(type)); |
michael@0 | 1263 | |
michael@0 | 1264 | return validCombo; |
michael@0 | 1265 | } |
michael@0 | 1266 | |
michael@0 | 1267 | /** |
michael@0 | 1268 | * Return true if format, type and jsArrayType are a valid combination. |
michael@0 | 1269 | * Also returns the size for texel of format and type (in bytes) via |
michael@0 | 1270 | * \a texelSize. |
michael@0 | 1271 | * |
michael@0 | 1272 | * It is assumed that type has previously been validated. |
michael@0 | 1273 | */ |
michael@0 | 1274 | bool |
michael@0 | 1275 | WebGLContext::ValidateTexInputData(GLenum type, int jsArrayType, WebGLTexImageFunc func) |
michael@0 | 1276 | { |
michael@0 | 1277 | bool validInput = false; |
michael@0 | 1278 | const char invalidTypedArray[] = "%s: invalid typed array type for given texture data type"; |
michael@0 | 1279 | |
michael@0 | 1280 | // First, we check for packed types |
michael@0 | 1281 | switch (type) { |
michael@0 | 1282 | case LOCAL_GL_UNSIGNED_BYTE: |
michael@0 | 1283 | validInput = (jsArrayType == -1 || jsArrayType == js::ArrayBufferView::TYPE_UINT8); |
michael@0 | 1284 | break; |
michael@0 | 1285 | |
michael@0 | 1286 | // TODO: WebGL spec doesn't allow half floats to specified as UInt16. |
michael@0 | 1287 | case LOCAL_GL_HALF_FLOAT: |
michael@0 | 1288 | case LOCAL_GL_HALF_FLOAT_OES: |
michael@0 | 1289 | validInput = (jsArrayType == -1); |
michael@0 | 1290 | break; |
michael@0 | 1291 | |
michael@0 | 1292 | case LOCAL_GL_UNSIGNED_SHORT: |
michael@0 | 1293 | case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4: |
michael@0 | 1294 | case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1: |
michael@0 | 1295 | case LOCAL_GL_UNSIGNED_SHORT_5_6_5: |
michael@0 | 1296 | validInput = (jsArrayType == -1 || jsArrayType == js::ArrayBufferView::TYPE_UINT16); |
michael@0 | 1297 | break; |
michael@0 | 1298 | |
michael@0 | 1299 | case LOCAL_GL_UNSIGNED_INT: |
michael@0 | 1300 | case LOCAL_GL_UNSIGNED_INT_24_8: |
michael@0 | 1301 | validInput = (jsArrayType == -1 || jsArrayType == js::ArrayBufferView::TYPE_UINT32); |
michael@0 | 1302 | break; |
michael@0 | 1303 | |
michael@0 | 1304 | case LOCAL_GL_FLOAT: |
michael@0 | 1305 | validInput = (jsArrayType == -1 || jsArrayType == js::ArrayBufferView::TYPE_FLOAT32); |
michael@0 | 1306 | break; |
michael@0 | 1307 | |
michael@0 | 1308 | default: |
michael@0 | 1309 | break; |
michael@0 | 1310 | } |
michael@0 | 1311 | |
michael@0 | 1312 | if (!validInput) |
michael@0 | 1313 | ErrorInvalidOperation(invalidTypedArray, InfoFrom(func)); |
michael@0 | 1314 | |
michael@0 | 1315 | return validInput; |
michael@0 | 1316 | } |
michael@0 | 1317 | |
michael@0 | 1318 | /** |
michael@0 | 1319 | * Test the gl(Copy|Compressed)?Tex[Sub]?Image[23]() parameters for errors. |
michael@0 | 1320 | * Verifies each of the parameters against the WebGL standard and enabled extensions. |
michael@0 | 1321 | */ |
michael@0 | 1322 | // TODO: Texture dims is here for future expansion in WebGL 2.0 |
michael@0 | 1323 | bool |
michael@0 | 1324 | WebGLContext::ValidateTexImage(GLuint dims, GLenum target, |
michael@0 | 1325 | GLint level, GLint internalFormat, |
michael@0 | 1326 | GLint xoffset, GLint yoffset, GLint zoffset, |
michael@0 | 1327 | GLint width, GLint height, GLint depth, |
michael@0 | 1328 | GLint border, GLenum format, GLenum type, |
michael@0 | 1329 | WebGLTexImageFunc func) |
michael@0 | 1330 | { |
michael@0 | 1331 | const char* info = InfoFrom(func); |
michael@0 | 1332 | |
michael@0 | 1333 | /* Check target */ |
michael@0 | 1334 | if (!ValidateTexImageTarget(dims, target, func)) |
michael@0 | 1335 | return false; |
michael@0 | 1336 | |
michael@0 | 1337 | /* Check level */ |
michael@0 | 1338 | if (level < 0) { |
michael@0 | 1339 | ErrorInvalidValue("%s: level must be >= 0", info); |
michael@0 | 1340 | return false; |
michael@0 | 1341 | } |
michael@0 | 1342 | |
michael@0 | 1343 | /* Check border */ |
michael@0 | 1344 | if (border != 0) { |
michael@0 | 1345 | ErrorInvalidValue("%s: border must be 0", info); |
michael@0 | 1346 | return false; |
michael@0 | 1347 | } |
michael@0 | 1348 | |
michael@0 | 1349 | /* Check incoming image format and type */ |
michael@0 | 1350 | if (!ValidateTexImageFormatAndType(format, type, func)) |
michael@0 | 1351 | return false; |
michael@0 | 1352 | |
michael@0 | 1353 | /* WebGL and OpenGL ES 2.0 impose additional restrictions on the |
michael@0 | 1354 | * combinations of format, internalFormat, and type that can be |
michael@0 | 1355 | * used. Formats and types that require additional extensions |
michael@0 | 1356 | * (e.g., GL_FLOAT requires GL_OES_texture_float) are filtered |
michael@0 | 1357 | * elsewhere. |
michael@0 | 1358 | */ |
michael@0 | 1359 | if ((GLint) format != internalFormat) { |
michael@0 | 1360 | ErrorInvalidOperation("%s: format does not match internalformat", info); |
michael@0 | 1361 | return false; |
michael@0 | 1362 | } |
michael@0 | 1363 | |
michael@0 | 1364 | /* check internalFormat */ |
michael@0 | 1365 | // TODO: Not sure if this is a bit of over kill. |
michael@0 | 1366 | if (BaseTexFormat(internalFormat) == LOCAL_GL_NONE) { |
michael@0 | 1367 | MOZ_ASSERT(false); |
michael@0 | 1368 | ErrorInvalidValue("%s:", info); |
michael@0 | 1369 | return false; |
michael@0 | 1370 | } |
michael@0 | 1371 | |
michael@0 | 1372 | /* Check texture image size */ |
michael@0 | 1373 | if (!ValidateTexImageSize(target, level, width, height, 0, func)) |
michael@0 | 1374 | return false; |
michael@0 | 1375 | |
michael@0 | 1376 | /* 5.14.8 Texture objects - WebGL Spec. |
michael@0 | 1377 | * "If an attempt is made to call these functions with no |
michael@0 | 1378 | * WebGLTexture bound (see above), an INVALID_OPERATION error |
michael@0 | 1379 | * is generated." |
michael@0 | 1380 | */ |
michael@0 | 1381 | WebGLTexture* tex = activeBoundTextureForTarget(target); |
michael@0 | 1382 | if (!tex) { |
michael@0 | 1383 | ErrorInvalidOperation("%s: no texture is bound to target %s", |
michael@0 | 1384 | info, NameFrom(target)); |
michael@0 | 1385 | return false; |
michael@0 | 1386 | } |
michael@0 | 1387 | |
michael@0 | 1388 | if (IsSubFunc(func)) { |
michael@0 | 1389 | if (!tex->HasImageInfoAt(target, level)) { |
michael@0 | 1390 | ErrorInvalidOperation("%s: no texture image previously defined for target %s at level %d", |
michael@0 | 1391 | info, NameFrom(target), level); |
michael@0 | 1392 | return false; |
michael@0 | 1393 | } |
michael@0 | 1394 | |
michael@0 | 1395 | const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(target, level); |
michael@0 | 1396 | if (!ValidateTexSubImageSize(xoffset, yoffset, zoffset, |
michael@0 | 1397 | width, height, depth, |
michael@0 | 1398 | imageInfo.Width(), imageInfo.Height(), 0, |
michael@0 | 1399 | func)) |
michael@0 | 1400 | { |
michael@0 | 1401 | return false; |
michael@0 | 1402 | } |
michael@0 | 1403 | |
michael@0 | 1404 | /* Require the format and type to match that of the existing |
michael@0 | 1405 | * texture as created |
michael@0 | 1406 | */ |
michael@0 | 1407 | if (imageInfo.WebGLFormat() != format || |
michael@0 | 1408 | imageInfo.WebGLType() != type) |
michael@0 | 1409 | { |
michael@0 | 1410 | ErrorInvalidOperation("%s: format or type doesn't match the existing texture", |
michael@0 | 1411 | info); |
michael@0 | 1412 | return false; |
michael@0 | 1413 | } |
michael@0 | 1414 | } |
michael@0 | 1415 | |
michael@0 | 1416 | /* Additional checks for depth textures */ |
michael@0 | 1417 | if (format == LOCAL_GL_DEPTH_COMPONENT || |
michael@0 | 1418 | format == LOCAL_GL_DEPTH_STENCIL) |
michael@0 | 1419 | { |
michael@0 | 1420 | if (func == WebGLTexImageFunc::TexSubImage || IsCopyFunc(func)) { |
michael@0 | 1421 | ErrorInvalidOperationWithName(this, "called with format/internalformat", |
michael@0 | 1422 | format, func); |
michael@0 | 1423 | return false; |
michael@0 | 1424 | } |
michael@0 | 1425 | |
michael@0 | 1426 | if (func == WebGLTexImageFunc::TexImage && |
michael@0 | 1427 | target != LOCAL_GL_TEXTURE_2D) |
michael@0 | 1428 | { |
michael@0 | 1429 | ErrorInvalidOperation("%s: with format of %s target must be TEXTURE_2D", |
michael@0 | 1430 | info, NameFrom(format)); |
michael@0 | 1431 | return false; |
michael@0 | 1432 | } |
michael@0 | 1433 | |
michael@0 | 1434 | if (func == WebGLTexImageFunc::TexImage && level != 0) { |
michael@0 | 1435 | ErrorInvalidOperation("%s: with format of %s target, level must be 0", |
michael@0 | 1436 | info, NameFrom(format)); |
michael@0 | 1437 | return false; |
michael@0 | 1438 | } |
michael@0 | 1439 | } |
michael@0 | 1440 | |
michael@0 | 1441 | /* Additional checks for compressed textures */ |
michael@0 | 1442 | if (!IsAllowedFromSource(format, func)) { |
michael@0 | 1443 | ErrorInvalidOperation("%s: Invalid format %s for this operation", |
michael@0 | 1444 | info, NameFrom(format)); |
michael@0 | 1445 | return false; |
michael@0 | 1446 | } |
michael@0 | 1447 | |
michael@0 | 1448 | /* Parameters are OK */ |
michael@0 | 1449 | return true; |
michael@0 | 1450 | } |
michael@0 | 1451 | |
michael@0 | 1452 | bool |
michael@0 | 1453 | WebGLContext::ValidateUniformLocation(const char* info, WebGLUniformLocation *location_object) |
michael@0 | 1454 | { |
michael@0 | 1455 | if (!ValidateObjectAllowNull(info, location_object)) |
michael@0 | 1456 | return false; |
michael@0 | 1457 | if (!location_object) |
michael@0 | 1458 | return false; |
michael@0 | 1459 | /* the need to check specifically for !mCurrentProgram here is explained in bug 657556 */ |
michael@0 | 1460 | if (!mCurrentProgram) { |
michael@0 | 1461 | ErrorInvalidOperation("%s: no program is currently bound", info); |
michael@0 | 1462 | return false; |
michael@0 | 1463 | } |
michael@0 | 1464 | if (mCurrentProgram != location_object->Program()) { |
michael@0 | 1465 | ErrorInvalidOperation("%s: this uniform location doesn't correspond to the current program", info); |
michael@0 | 1466 | return false; |
michael@0 | 1467 | } |
michael@0 | 1468 | if (mCurrentProgram->Generation() != location_object->ProgramGeneration()) { |
michael@0 | 1469 | ErrorInvalidOperation("%s: This uniform location is obsolete since the program has been relinked", info); |
michael@0 | 1470 | return false; |
michael@0 | 1471 | } |
michael@0 | 1472 | return true; |
michael@0 | 1473 | } |
michael@0 | 1474 | |
michael@0 | 1475 | bool |
michael@0 | 1476 | WebGLContext::ValidateSamplerUniformSetter(const char* info, WebGLUniformLocation *location, GLint value) |
michael@0 | 1477 | { |
michael@0 | 1478 | if (location->Info().type != SH_SAMPLER_2D && |
michael@0 | 1479 | location->Info().type != SH_SAMPLER_CUBE) |
michael@0 | 1480 | { |
michael@0 | 1481 | return true; |
michael@0 | 1482 | } |
michael@0 | 1483 | |
michael@0 | 1484 | if (value >= 0 && value < mGLMaxTextureUnits) |
michael@0 | 1485 | return true; |
michael@0 | 1486 | |
michael@0 | 1487 | ErrorInvalidValue("%s: this uniform location is a sampler, but %d is not a valid texture unit", |
michael@0 | 1488 | info, value); |
michael@0 | 1489 | return false; |
michael@0 | 1490 | } |
michael@0 | 1491 | |
michael@0 | 1492 | bool |
michael@0 | 1493 | WebGLContext::ValidateAttribArraySetter(const char* name, uint32_t cnt, uint32_t arrayLength) |
michael@0 | 1494 | { |
michael@0 | 1495 | if (IsContextLost()) { |
michael@0 | 1496 | return false; |
michael@0 | 1497 | } |
michael@0 | 1498 | if (arrayLength < cnt) { |
michael@0 | 1499 | ErrorInvalidOperation("%s: array must be >= %d elements", name, cnt); |
michael@0 | 1500 | return false; |
michael@0 | 1501 | } |
michael@0 | 1502 | return true; |
michael@0 | 1503 | } |
michael@0 | 1504 | |
michael@0 | 1505 | bool |
michael@0 | 1506 | WebGLContext::ValidateUniformArraySetter(const char* name, uint32_t expectedElemSize, WebGLUniformLocation *location_object, |
michael@0 | 1507 | GLint& location, uint32_t& numElementsToUpload, uint32_t arrayLength) |
michael@0 | 1508 | { |
michael@0 | 1509 | if (IsContextLost()) |
michael@0 | 1510 | return false; |
michael@0 | 1511 | if (!ValidateUniformLocation(name, location_object)) |
michael@0 | 1512 | return false; |
michael@0 | 1513 | location = location_object->Location(); |
michael@0 | 1514 | uint32_t uniformElemSize = location_object->ElementSize(); |
michael@0 | 1515 | if (expectedElemSize != uniformElemSize) { |
michael@0 | 1516 | ErrorInvalidOperation("%s: this function expected a uniform of element size %d," |
michael@0 | 1517 | " got a uniform of element size %d", name, |
michael@0 | 1518 | expectedElemSize, |
michael@0 | 1519 | uniformElemSize); |
michael@0 | 1520 | return false; |
michael@0 | 1521 | } |
michael@0 | 1522 | if (arrayLength == 0 || |
michael@0 | 1523 | arrayLength % expectedElemSize) |
michael@0 | 1524 | { |
michael@0 | 1525 | ErrorInvalidValue("%s: expected an array of length a multiple" |
michael@0 | 1526 | " of %d, got an array of length %d", name, |
michael@0 | 1527 | expectedElemSize, |
michael@0 | 1528 | arrayLength); |
michael@0 | 1529 | return false; |
michael@0 | 1530 | } |
michael@0 | 1531 | const WebGLUniformInfo& info = location_object->Info(); |
michael@0 | 1532 | if (!info.isArray && |
michael@0 | 1533 | arrayLength != expectedElemSize) { |
michael@0 | 1534 | ErrorInvalidOperation("%s: expected an array of length exactly" |
michael@0 | 1535 | " %d (since this uniform is not an array" |
michael@0 | 1536 | " uniform), got an array of length %d", name, |
michael@0 | 1537 | expectedElemSize, |
michael@0 | 1538 | arrayLength); |
michael@0 | 1539 | return false; |
michael@0 | 1540 | } |
michael@0 | 1541 | numElementsToUpload = |
michael@0 | 1542 | std::min(info.arraySize, arrayLength / expectedElemSize); |
michael@0 | 1543 | return true; |
michael@0 | 1544 | } |
michael@0 | 1545 | |
michael@0 | 1546 | bool |
michael@0 | 1547 | WebGLContext::ValidateUniformMatrixArraySetter(const char* name, int dim, WebGLUniformLocation *location_object, |
michael@0 | 1548 | GLint& location, uint32_t& numElementsToUpload, uint32_t arrayLength, |
michael@0 | 1549 | WebGLboolean aTranspose) |
michael@0 | 1550 | { |
michael@0 | 1551 | uint32_t expectedElemSize = (dim)*(dim); |
michael@0 | 1552 | if (IsContextLost()) |
michael@0 | 1553 | return false; |
michael@0 | 1554 | if (!ValidateUniformLocation(name, location_object)) |
michael@0 | 1555 | return false; |
michael@0 | 1556 | location = location_object->Location(); |
michael@0 | 1557 | uint32_t uniformElemSize = location_object->ElementSize(); |
michael@0 | 1558 | if (expectedElemSize != uniformElemSize) { |
michael@0 | 1559 | ErrorInvalidOperation("%s: this function expected a uniform of element size %d," |
michael@0 | 1560 | " got a uniform of element size %d", name, |
michael@0 | 1561 | expectedElemSize, |
michael@0 | 1562 | uniformElemSize); |
michael@0 | 1563 | return false; |
michael@0 | 1564 | } |
michael@0 | 1565 | if (arrayLength == 0 || |
michael@0 | 1566 | arrayLength % expectedElemSize) |
michael@0 | 1567 | { |
michael@0 | 1568 | ErrorInvalidValue("%s: expected an array of length a multiple" |
michael@0 | 1569 | " of %d, got an array of length %d", name, |
michael@0 | 1570 | expectedElemSize, |
michael@0 | 1571 | arrayLength); |
michael@0 | 1572 | return false; |
michael@0 | 1573 | } |
michael@0 | 1574 | const WebGLUniformInfo& info = location_object->Info(); |
michael@0 | 1575 | if (!info.isArray && |
michael@0 | 1576 | arrayLength != expectedElemSize) { |
michael@0 | 1577 | ErrorInvalidOperation("%s: expected an array of length exactly" |
michael@0 | 1578 | " %d (since this uniform is not an array" |
michael@0 | 1579 | " uniform), got an array of length %d", name, |
michael@0 | 1580 | expectedElemSize, |
michael@0 | 1581 | arrayLength); |
michael@0 | 1582 | return false; |
michael@0 | 1583 | } |
michael@0 | 1584 | if (aTranspose) { |
michael@0 | 1585 | ErrorInvalidValue("%s: transpose must be FALSE as per the " |
michael@0 | 1586 | "OpenGL ES 2.0 spec", name); |
michael@0 | 1587 | return false; |
michael@0 | 1588 | } |
michael@0 | 1589 | numElementsToUpload = |
michael@0 | 1590 | std::min(info.arraySize, arrayLength / (expectedElemSize)); |
michael@0 | 1591 | return true; |
michael@0 | 1592 | } |
michael@0 | 1593 | |
michael@0 | 1594 | bool |
michael@0 | 1595 | WebGLContext::ValidateUniformSetter(const char* name, WebGLUniformLocation *location_object, GLint& location) |
michael@0 | 1596 | { |
michael@0 | 1597 | if (IsContextLost()) |
michael@0 | 1598 | return false; |
michael@0 | 1599 | if (!ValidateUniformLocation(name, location_object)) |
michael@0 | 1600 | return false; |
michael@0 | 1601 | location = location_object->Location(); |
michael@0 | 1602 | return true; |
michael@0 | 1603 | } |
michael@0 | 1604 | |
michael@0 | 1605 | bool WebGLContext::ValidateAttribIndex(GLuint index, const char *info) |
michael@0 | 1606 | { |
michael@0 | 1607 | return mBoundVertexArray->EnsureAttrib(index, info); |
michael@0 | 1608 | } |
michael@0 | 1609 | |
michael@0 | 1610 | bool WebGLContext::ValidateStencilParamsForDrawCall() |
michael@0 | 1611 | { |
michael@0 | 1612 | const char *msg = "%s set different front and back stencil %s. Drawing in this configuration is not allowed."; |
michael@0 | 1613 | if (mStencilRefFront != mStencilRefBack) { |
michael@0 | 1614 | ErrorInvalidOperation(msg, "stencilFuncSeparate", "reference values"); |
michael@0 | 1615 | return false; |
michael@0 | 1616 | } |
michael@0 | 1617 | if (mStencilValueMaskFront != mStencilValueMaskBack) { |
michael@0 | 1618 | ErrorInvalidOperation(msg, "stencilFuncSeparate", "value masks"); |
michael@0 | 1619 | return false; |
michael@0 | 1620 | } |
michael@0 | 1621 | if (mStencilWriteMaskFront != mStencilWriteMaskBack) { |
michael@0 | 1622 | ErrorInvalidOperation(msg, "stencilMaskSeparate", "write masks"); |
michael@0 | 1623 | return false; |
michael@0 | 1624 | } |
michael@0 | 1625 | return true; |
michael@0 | 1626 | } |
michael@0 | 1627 | |
michael@0 | 1628 | static inline int32_t floorPOT(int32_t x) |
michael@0 | 1629 | { |
michael@0 | 1630 | MOZ_ASSERT(x > 0); |
michael@0 | 1631 | int32_t pot = 1; |
michael@0 | 1632 | while (pot < 0x40000000) { |
michael@0 | 1633 | if (x < pot*2) |
michael@0 | 1634 | break; |
michael@0 | 1635 | pot *= 2; |
michael@0 | 1636 | } |
michael@0 | 1637 | return pot; |
michael@0 | 1638 | } |
michael@0 | 1639 | |
michael@0 | 1640 | bool |
michael@0 | 1641 | WebGLContext::InitAndValidateGL() |
michael@0 | 1642 | { |
michael@0 | 1643 | if (!gl) return false; |
michael@0 | 1644 | |
michael@0 | 1645 | GLenum error = gl->fGetError(); |
michael@0 | 1646 | if (error != LOCAL_GL_NO_ERROR) { |
michael@0 | 1647 | GenerateWarning("GL error 0x%x occurred during OpenGL context initialization, before WebGL initialization!", error); |
michael@0 | 1648 | return false; |
michael@0 | 1649 | } |
michael@0 | 1650 | |
michael@0 | 1651 | mMinCapability = Preferences::GetBool("webgl.min_capability_mode", false); |
michael@0 | 1652 | mDisableExtensions = Preferences::GetBool("webgl.disable-extensions", false); |
michael@0 | 1653 | mLoseContextOnHeapMinimize = Preferences::GetBool("webgl.lose-context-on-heap-minimize", false); |
michael@0 | 1654 | mCanLoseContextInForeground = Preferences::GetBool("webgl.can-lose-context-in-foreground", true); |
michael@0 | 1655 | |
michael@0 | 1656 | if (MinCapabilityMode()) { |
michael@0 | 1657 | mDisableFragHighP = true; |
michael@0 | 1658 | } |
michael@0 | 1659 | |
michael@0 | 1660 | // These are the default values, see 6.2 State tables in the |
michael@0 | 1661 | // OpenGL ES 2.0.25 spec. |
michael@0 | 1662 | mColorWriteMask[0] = 1; |
michael@0 | 1663 | mColorWriteMask[1] = 1; |
michael@0 | 1664 | mColorWriteMask[2] = 1; |
michael@0 | 1665 | mColorWriteMask[3] = 1; |
michael@0 | 1666 | mDepthWriteMask = 1; |
michael@0 | 1667 | mColorClearValue[0] = 0.f; |
michael@0 | 1668 | mColorClearValue[1] = 0.f; |
michael@0 | 1669 | mColorClearValue[2] = 0.f; |
michael@0 | 1670 | mColorClearValue[3] = 0.f; |
michael@0 | 1671 | mDepthClearValue = 1.f; |
michael@0 | 1672 | mStencilClearValue = 0; |
michael@0 | 1673 | mStencilRefFront = 0; |
michael@0 | 1674 | mStencilRefBack = 0; |
michael@0 | 1675 | mStencilValueMaskFront = 0xffffffff; |
michael@0 | 1676 | mStencilValueMaskBack = 0xffffffff; |
michael@0 | 1677 | mStencilWriteMaskFront = 0xffffffff; |
michael@0 | 1678 | mStencilWriteMaskBack = 0xffffffff; |
michael@0 | 1679 | |
michael@0 | 1680 | // Bindings, etc. |
michael@0 | 1681 | mActiveTexture = 0; |
michael@0 | 1682 | mEmitContextLostErrorOnce = true; |
michael@0 | 1683 | mWebGLError = LOCAL_GL_NO_ERROR; |
michael@0 | 1684 | mUnderlyingGLError = LOCAL_GL_NO_ERROR; |
michael@0 | 1685 | |
michael@0 | 1686 | mBound2DTextures.Clear(); |
michael@0 | 1687 | mBoundCubeMapTextures.Clear(); |
michael@0 | 1688 | |
michael@0 | 1689 | mBoundArrayBuffer = nullptr; |
michael@0 | 1690 | mBoundTransformFeedbackBuffer = nullptr; |
michael@0 | 1691 | mCurrentProgram = nullptr; |
michael@0 | 1692 | |
michael@0 | 1693 | mBoundFramebuffer = nullptr; |
michael@0 | 1694 | mBoundRenderbuffer = nullptr; |
michael@0 | 1695 | |
michael@0 | 1696 | MakeContextCurrent(); |
michael@0 | 1697 | |
michael@0 | 1698 | // on desktop OpenGL, we always keep vertex attrib 0 array enabled |
michael@0 | 1699 | if (!gl->IsGLES()) { |
michael@0 | 1700 | gl->fEnableVertexAttribArray(0); |
michael@0 | 1701 | } |
michael@0 | 1702 | |
michael@0 | 1703 | if (MinCapabilityMode()) { |
michael@0 | 1704 | mGLMaxVertexAttribs = MINVALUE_GL_MAX_VERTEX_ATTRIBS; |
michael@0 | 1705 | } else { |
michael@0 | 1706 | gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_ATTRIBS, &mGLMaxVertexAttribs); |
michael@0 | 1707 | } |
michael@0 | 1708 | if (mGLMaxVertexAttribs < 8) { |
michael@0 | 1709 | GenerateWarning("GL_MAX_VERTEX_ATTRIBS: %d is < 8!", mGLMaxVertexAttribs); |
michael@0 | 1710 | return false; |
michael@0 | 1711 | } |
michael@0 | 1712 | |
michael@0 | 1713 | // Note: GL_MAX_TEXTURE_UNITS is fixed at 4 for most desktop hardware, |
michael@0 | 1714 | // even though the hardware supports much more. The |
michael@0 | 1715 | // GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS value is the accurate value. |
michael@0 | 1716 | if (MinCapabilityMode()) { |
michael@0 | 1717 | mGLMaxTextureUnits = MINVALUE_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS; |
michael@0 | 1718 | } else { |
michael@0 | 1719 | gl->fGetIntegerv(LOCAL_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &mGLMaxTextureUnits); |
michael@0 | 1720 | } |
michael@0 | 1721 | if (mGLMaxTextureUnits < 8) { |
michael@0 | 1722 | GenerateWarning("GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: %d is < 8!", mGLMaxTextureUnits); |
michael@0 | 1723 | return false; |
michael@0 | 1724 | } |
michael@0 | 1725 | |
michael@0 | 1726 | mBound2DTextures.SetLength(mGLMaxTextureUnits); |
michael@0 | 1727 | mBoundCubeMapTextures.SetLength(mGLMaxTextureUnits); |
michael@0 | 1728 | |
michael@0 | 1729 | if (MinCapabilityMode()) { |
michael@0 | 1730 | mGLMaxTextureSize = MINVALUE_GL_MAX_TEXTURE_SIZE; |
michael@0 | 1731 | mGLMaxCubeMapTextureSize = MINVALUE_GL_MAX_CUBE_MAP_TEXTURE_SIZE; |
michael@0 | 1732 | mGLMaxRenderbufferSize = MINVALUE_GL_MAX_RENDERBUFFER_SIZE; |
michael@0 | 1733 | mGLMaxTextureImageUnits = MINVALUE_GL_MAX_TEXTURE_IMAGE_UNITS; |
michael@0 | 1734 | mGLMaxVertexTextureImageUnits = MINVALUE_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS; |
michael@0 | 1735 | } else { |
michael@0 | 1736 | gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, &mGLMaxTextureSize); |
michael@0 | 1737 | gl->fGetIntegerv(LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE, &mGLMaxCubeMapTextureSize); |
michael@0 | 1738 | gl->fGetIntegerv(LOCAL_GL_MAX_RENDERBUFFER_SIZE, &mGLMaxRenderbufferSize); |
michael@0 | 1739 | gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS, &mGLMaxTextureImageUnits); |
michael@0 | 1740 | gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &mGLMaxVertexTextureImageUnits); |
michael@0 | 1741 | } |
michael@0 | 1742 | |
michael@0 | 1743 | mGLMaxTextureSize = floorPOT(mGLMaxTextureSize); |
michael@0 | 1744 | mGLMaxRenderbufferSize = floorPOT(mGLMaxRenderbufferSize); |
michael@0 | 1745 | |
michael@0 | 1746 | if (MinCapabilityMode()) { |
michael@0 | 1747 | mGLMaxFragmentUniformVectors = MINVALUE_GL_MAX_FRAGMENT_UNIFORM_VECTORS; |
michael@0 | 1748 | mGLMaxVertexUniformVectors = MINVALUE_GL_MAX_VERTEX_UNIFORM_VECTORS; |
michael@0 | 1749 | mGLMaxVaryingVectors = MINVALUE_GL_MAX_VARYING_VECTORS; |
michael@0 | 1750 | } else { |
michael@0 | 1751 | if (gl->IsSupported(gl::GLFeature::ES2_compatibility)) { |
michael@0 | 1752 | gl->fGetIntegerv(LOCAL_GL_MAX_FRAGMENT_UNIFORM_VECTORS, &mGLMaxFragmentUniformVectors); |
michael@0 | 1753 | gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_UNIFORM_VECTORS, &mGLMaxVertexUniformVectors); |
michael@0 | 1754 | gl->fGetIntegerv(LOCAL_GL_MAX_VARYING_VECTORS, &mGLMaxVaryingVectors); |
michael@0 | 1755 | } else { |
michael@0 | 1756 | gl->fGetIntegerv(LOCAL_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &mGLMaxFragmentUniformVectors); |
michael@0 | 1757 | mGLMaxFragmentUniformVectors /= 4; |
michael@0 | 1758 | gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_UNIFORM_COMPONENTS, &mGLMaxVertexUniformVectors); |
michael@0 | 1759 | mGLMaxVertexUniformVectors /= 4; |
michael@0 | 1760 | |
michael@0 | 1761 | // we are now going to try to read GL_MAX_VERTEX_OUTPUT_COMPONENTS and GL_MAX_FRAGMENT_INPUT_COMPONENTS, |
michael@0 | 1762 | // however these constants only entered the OpenGL standard at OpenGL 3.2. So we will try reading, |
michael@0 | 1763 | // and check OpenGL error for INVALID_ENUM. |
michael@0 | 1764 | |
michael@0 | 1765 | // before we start, we check that no error already occurred, to prevent hiding it in our subsequent error handling |
michael@0 | 1766 | error = gl->GetAndClearError(); |
michael@0 | 1767 | if (error != LOCAL_GL_NO_ERROR) { |
michael@0 | 1768 | GenerateWarning("GL error 0x%x occurred during WebGL context initialization!", error); |
michael@0 | 1769 | return false; |
michael@0 | 1770 | } |
michael@0 | 1771 | |
michael@0 | 1772 | // On the public_webgl list, "problematic GetParameter pnames" thread, the following formula was given: |
michael@0 | 1773 | // mGLMaxVaryingVectors = min (GL_MAX_VERTEX_OUTPUT_COMPONENTS, GL_MAX_FRAGMENT_INPUT_COMPONENTS) / 4 |
michael@0 | 1774 | GLint maxVertexOutputComponents, |
michael@0 | 1775 | minFragmentInputComponents; |
michael@0 | 1776 | gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_OUTPUT_COMPONENTS, &maxVertexOutputComponents); |
michael@0 | 1777 | gl->fGetIntegerv(LOCAL_GL_MAX_FRAGMENT_INPUT_COMPONENTS, &minFragmentInputComponents); |
michael@0 | 1778 | |
michael@0 | 1779 | error = gl->GetAndClearError(); |
michael@0 | 1780 | switch (error) { |
michael@0 | 1781 | case LOCAL_GL_NO_ERROR: |
michael@0 | 1782 | mGLMaxVaryingVectors = std::min(maxVertexOutputComponents, minFragmentInputComponents) / 4; |
michael@0 | 1783 | break; |
michael@0 | 1784 | case LOCAL_GL_INVALID_ENUM: |
michael@0 | 1785 | mGLMaxVaryingVectors = 16; // = 64/4, 64 is the min value for maxVertexOutputComponents in OpenGL 3.2 spec |
michael@0 | 1786 | break; |
michael@0 | 1787 | default: |
michael@0 | 1788 | GenerateWarning("GL error 0x%x occurred during WebGL context initialization!", error); |
michael@0 | 1789 | return false; |
michael@0 | 1790 | } |
michael@0 | 1791 | } |
michael@0 | 1792 | } |
michael@0 | 1793 | |
michael@0 | 1794 | // Always 1 for GLES2 |
michael@0 | 1795 | mMaxFramebufferColorAttachments = 1; |
michael@0 | 1796 | |
michael@0 | 1797 | if (!gl->IsGLES()) { |
michael@0 | 1798 | // gl_PointSize is always available in ES2 GLSL, but has to be |
michael@0 | 1799 | // specifically enabled on desktop GLSL. |
michael@0 | 1800 | gl->fEnable(LOCAL_GL_VERTEX_PROGRAM_POINT_SIZE); |
michael@0 | 1801 | |
michael@0 | 1802 | // gl_PointCoord is always available in ES2 GLSL and in newer desktop GLSL versions, but apparently |
michael@0 | 1803 | // not in OpenGL 2 and apparently not (due to a driver bug) on certain NVIDIA setups. See: |
michael@0 | 1804 | // http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=261472 |
michael@0 | 1805 | // Note that this used to cause crashes on old ATI drivers... hopefully not a significant |
michael@0 | 1806 | // problem anymore. See bug 602183. |
michael@0 | 1807 | gl->fEnable(LOCAL_GL_POINT_SPRITE); |
michael@0 | 1808 | } |
michael@0 | 1809 | |
michael@0 | 1810 | #ifdef XP_MACOSX |
michael@0 | 1811 | if (gl->WorkAroundDriverBugs() && |
michael@0 | 1812 | gl->Vendor() == gl::GLVendor::ATI) { |
michael@0 | 1813 | // The Mac ATI driver, in all known OSX version up to and including 10.8, |
michael@0 | 1814 | // renders points sprites upside-down. Apple bug 11778921 |
michael@0 | 1815 | gl->fPointParameterf(LOCAL_GL_POINT_SPRITE_COORD_ORIGIN, LOCAL_GL_LOWER_LEFT); |
michael@0 | 1816 | } |
michael@0 | 1817 | #endif |
michael@0 | 1818 | |
michael@0 | 1819 | // Check the shader validator pref |
michael@0 | 1820 | NS_ENSURE_TRUE(Preferences::GetRootBranch(), false); |
michael@0 | 1821 | |
michael@0 | 1822 | mShaderValidation = |
michael@0 | 1823 | Preferences::GetBool("webgl.shader_validator", mShaderValidation); |
michael@0 | 1824 | |
michael@0 | 1825 | // initialize shader translator |
michael@0 | 1826 | if (mShaderValidation) { |
michael@0 | 1827 | if (!ShInitialize()) { |
michael@0 | 1828 | GenerateWarning("GLSL translator initialization failed!"); |
michael@0 | 1829 | return false; |
michael@0 | 1830 | } |
michael@0 | 1831 | } |
michael@0 | 1832 | |
michael@0 | 1833 | // Mesa can only be detected with the GL_VERSION string, of the form "2.1 Mesa 7.11.0" |
michael@0 | 1834 | mIsMesa = strstr((const char *)(gl->fGetString(LOCAL_GL_VERSION)), "Mesa"); |
michael@0 | 1835 | |
michael@0 | 1836 | // notice that the point of calling GetAndClearError here is not only to check for error, |
michael@0 | 1837 | // it is also to reset the error flags so that a subsequent WebGL getError call will give the correct result. |
michael@0 | 1838 | error = gl->GetAndClearError(); |
michael@0 | 1839 | if (error != LOCAL_GL_NO_ERROR) { |
michael@0 | 1840 | GenerateWarning("GL error 0x%x occurred during WebGL context initialization!", error); |
michael@0 | 1841 | return false; |
michael@0 | 1842 | } |
michael@0 | 1843 | |
michael@0 | 1844 | if (IsWebGL2() && |
michael@0 | 1845 | !InitWebGL2()) |
michael@0 | 1846 | { |
michael@0 | 1847 | // Todo: Bug 898404: Only allow WebGL2 on GL>=3.0 on desktop GL. |
michael@0 | 1848 | return false; |
michael@0 | 1849 | } |
michael@0 | 1850 | |
michael@0 | 1851 | mMemoryPressureObserver |
michael@0 | 1852 | = new WebGLMemoryPressureObserver(this); |
michael@0 | 1853 | nsCOMPtr<nsIObserverService> observerService |
michael@0 | 1854 | = mozilla::services::GetObserverService(); |
michael@0 | 1855 | if (observerService) { |
michael@0 | 1856 | observerService->AddObserver(mMemoryPressureObserver, |
michael@0 | 1857 | "memory-pressure", |
michael@0 | 1858 | false); |
michael@0 | 1859 | } |
michael@0 | 1860 | |
michael@0 | 1861 | mDefaultVertexArray = new WebGLVertexArray(this); |
michael@0 | 1862 | mDefaultVertexArray->mAttribs.SetLength(mGLMaxVertexAttribs); |
michael@0 | 1863 | mBoundVertexArray = mDefaultVertexArray; |
michael@0 | 1864 | |
michael@0 | 1865 | return true; |
michael@0 | 1866 | } |