Bumping gaia.json for 3 gaia-central revision(s) a=gaia-bump
[gecko.git] / content / canvas / src / WebGLContextValidate.cpp
blob54a92279d85192bfe0c9a441c98b3c7a622fe262
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "WebGLContext.h"
7 #include "WebGLBuffer.h"
8 #include "WebGLVertexAttribData.h"
9 #include "WebGLShader.h"
10 #include "WebGLProgram.h"
11 #include "WebGLUniformLocation.h"
12 #include "WebGLFramebuffer.h"
13 #include "WebGLRenderbuffer.h"
14 #include "WebGLTexture.h"
15 #include "WebGLVertexArray.h"
16 #include "GLContext.h"
18 #include "mozilla/CheckedInt.h"
19 #include "mozilla/Preferences.h"
20 #include "mozilla/Services.h"
22 #include "jsfriendapi.h"
24 #include "angle/ShaderLang.h"
26 #include <algorithm>
28 #include "mozilla/Services.h"
29 #include "nsIObserverService.h"
31 using namespace mozilla;
34 * Pull data out of the program, post-linking
36 bool
37 WebGLProgram::UpdateInfo()
39 mIdentifierMap = nullptr;
40 mIdentifierReverseMap = nullptr;
41 mUniformInfoMap = nullptr;
43 mAttribMaxNameLength = 0;
45 for (size_t i = 0; i < mAttachedShaders.Length(); i++)
46 mAttribMaxNameLength = std::max(mAttribMaxNameLength, mAttachedShaders[i]->mAttribMaxNameLength);
48 GLint attribCount;
49 mContext->gl->fGetProgramiv(mGLName, LOCAL_GL_ACTIVE_ATTRIBUTES, &attribCount);
51 if (!mAttribsInUse.SetLength(mContext->mGLMaxVertexAttribs)) {
52 mContext->ErrorOutOfMemory("updateInfo: out of memory to allocate %d attribs", mContext->mGLMaxVertexAttribs);
53 return false;
56 for (size_t i = 0; i < mAttribsInUse.Length(); i++)
57 mAttribsInUse[i] = false;
59 nsAutoArrayPtr<char> nameBuf(new char[mAttribMaxNameLength]);
61 for (int i = 0; i < attribCount; ++i) {
62 GLint attrnamelen;
63 GLint attrsize;
64 GLenum attrtype;
65 mContext->gl->fGetActiveAttrib(mGLName, i, mAttribMaxNameLength, &attrnamelen, &attrsize, &attrtype, nameBuf);
66 if (attrnamelen > 0) {
67 GLint loc = mContext->gl->fGetAttribLocation(mGLName, nameBuf);
68 MOZ_ASSERT(loc >= 0, "major oops in managing the attributes of a WebGL program");
69 if (loc < mContext->mGLMaxVertexAttribs) {
70 mAttribsInUse[loc] = true;
71 } else {
72 mContext->GenerateWarning("program exceeds MAX_VERTEX_ATTRIBS");
73 return false;
78 if (!mUniformInfoMap) {
79 mUniformInfoMap = new CStringToUniformInfoMap;
80 for (size_t i = 0; i < mAttachedShaders.Length(); i++) {
81 for (size_t j = 0; j < mAttachedShaders[i]->mUniforms.Length(); j++) {
82 const WebGLMappedIdentifier& uniform = mAttachedShaders[i]->mUniforms[j];
83 const WebGLUniformInfo& info = mAttachedShaders[i]->mUniformInfos[j];
84 mUniformInfoMap->Put(uniform.mapped, info);
89 mActiveAttribMap.clear();
91 GLint numActiveAttrs = 0;
92 mContext->gl->fGetProgramiv(mGLName, LOCAL_GL_ACTIVE_ATTRIBUTES, &numActiveAttrs);
94 // Spec says the maximum attrib name length is 256 chars, so this is
95 // sufficient to hold any attrib name.
96 char attrName[257];
98 GLint dummySize;
99 GLenum dummyType;
100 for (GLint i = 0; i < numActiveAttrs; i++) {
101 mContext->gl->fGetActiveAttrib(mGLName, i, 257, nullptr, &dummySize,
102 &dummyType, attrName);
103 GLint attrLoc = mContext->gl->fGetAttribLocation(mGLName, attrName);
104 MOZ_ASSERT(attrLoc >= 0);
105 mActiveAttribMap.insert(std::make_pair(attrLoc, nsCString(attrName)));
108 return true;
111 bool WebGLContext::ValidateBlendEquationEnum(GLenum mode, const char *info)
113 switch (mode) {
114 case LOCAL_GL_FUNC_ADD:
115 case LOCAL_GL_FUNC_SUBTRACT:
116 case LOCAL_GL_FUNC_REVERSE_SUBTRACT:
117 return true;
118 case LOCAL_GL_MIN:
119 case LOCAL_GL_MAX:
120 if (IsWebGL2()) {
121 // http://www.opengl.org/registry/specs/EXT/blend_minmax.txt
122 return true;
124 break;
125 default:
126 break;
129 ErrorInvalidEnumInfo(info, mode);
130 return false;
133 bool WebGLContext::ValidateBlendFuncDstEnum(GLenum factor, const char *info)
135 switch (factor) {
136 case LOCAL_GL_ZERO:
137 case LOCAL_GL_ONE:
138 case LOCAL_GL_SRC_COLOR:
139 case LOCAL_GL_ONE_MINUS_SRC_COLOR:
140 case LOCAL_GL_DST_COLOR:
141 case LOCAL_GL_ONE_MINUS_DST_COLOR:
142 case LOCAL_GL_SRC_ALPHA:
143 case LOCAL_GL_ONE_MINUS_SRC_ALPHA:
144 case LOCAL_GL_DST_ALPHA:
145 case LOCAL_GL_ONE_MINUS_DST_ALPHA:
146 case LOCAL_GL_CONSTANT_COLOR:
147 case LOCAL_GL_ONE_MINUS_CONSTANT_COLOR:
148 case LOCAL_GL_CONSTANT_ALPHA:
149 case LOCAL_GL_ONE_MINUS_CONSTANT_ALPHA:
150 return true;
151 default:
152 ErrorInvalidEnumInfo(info, factor);
153 return false;
157 bool WebGLContext::ValidateBlendFuncSrcEnum(GLenum factor, const char *info)
159 if (factor == LOCAL_GL_SRC_ALPHA_SATURATE)
160 return true;
161 else
162 return ValidateBlendFuncDstEnum(factor, info);
165 bool WebGLContext::ValidateBlendFuncEnumsCompatibility(GLenum sfactor, GLenum dfactor, const char *info)
167 bool sfactorIsConstantColor = sfactor == LOCAL_GL_CONSTANT_COLOR ||
168 sfactor == LOCAL_GL_ONE_MINUS_CONSTANT_COLOR;
169 bool sfactorIsConstantAlpha = sfactor == LOCAL_GL_CONSTANT_ALPHA ||
170 sfactor == LOCAL_GL_ONE_MINUS_CONSTANT_ALPHA;
171 bool dfactorIsConstantColor = dfactor == LOCAL_GL_CONSTANT_COLOR ||
172 dfactor == LOCAL_GL_ONE_MINUS_CONSTANT_COLOR;
173 bool dfactorIsConstantAlpha = dfactor == LOCAL_GL_CONSTANT_ALPHA ||
174 dfactor == LOCAL_GL_ONE_MINUS_CONSTANT_ALPHA;
175 if ( (sfactorIsConstantColor && dfactorIsConstantAlpha) ||
176 (dfactorIsConstantColor && sfactorIsConstantAlpha) ) {
177 ErrorInvalidOperation("%s are mutually incompatible, see section 6.8 in the WebGL 1.0 spec", info);
178 return false;
179 } else {
180 return true;
184 bool WebGLContext::ValidateTextureTargetEnum(GLenum target, const char *info)
186 switch (target) {
187 case LOCAL_GL_TEXTURE_2D:
188 case LOCAL_GL_TEXTURE_CUBE_MAP:
189 return true;
190 default:
191 ErrorInvalidEnumInfo(info, target);
192 return false;
196 bool WebGLContext::ValidateComparisonEnum(GLenum target, const char *info)
198 switch (target) {
199 case LOCAL_GL_NEVER:
200 case LOCAL_GL_LESS:
201 case LOCAL_GL_LEQUAL:
202 case LOCAL_GL_GREATER:
203 case LOCAL_GL_GEQUAL:
204 case LOCAL_GL_EQUAL:
205 case LOCAL_GL_NOTEQUAL:
206 case LOCAL_GL_ALWAYS:
207 return true;
208 default:
209 ErrorInvalidEnumInfo(info, target);
210 return false;
214 bool WebGLContext::ValidateStencilOpEnum(GLenum action, const char *info)
216 switch (action) {
217 case LOCAL_GL_KEEP:
218 case LOCAL_GL_ZERO:
219 case LOCAL_GL_REPLACE:
220 case LOCAL_GL_INCR:
221 case LOCAL_GL_INCR_WRAP:
222 case LOCAL_GL_DECR:
223 case LOCAL_GL_DECR_WRAP:
224 case LOCAL_GL_INVERT:
225 return true;
226 default:
227 ErrorInvalidEnumInfo(info, action);
228 return false;
232 bool WebGLContext::ValidateFaceEnum(GLenum face, const char *info)
234 switch (face) {
235 case LOCAL_GL_FRONT:
236 case LOCAL_GL_BACK:
237 case LOCAL_GL_FRONT_AND_BACK:
238 return true;
239 default:
240 ErrorInvalidEnumInfo(info, face);
241 return false;
245 bool WebGLContext::ValidateDrawModeEnum(GLenum mode, const char *info)
247 switch (mode) {
248 case LOCAL_GL_TRIANGLES:
249 case LOCAL_GL_TRIANGLE_STRIP:
250 case LOCAL_GL_TRIANGLE_FAN:
251 case LOCAL_GL_POINTS:
252 case LOCAL_GL_LINE_STRIP:
253 case LOCAL_GL_LINE_LOOP:
254 case LOCAL_GL_LINES:
255 return true;
256 default:
257 ErrorInvalidEnumInfo(info, mode);
258 return false;
262 bool WebGLContext::ValidateGLSLVariableName(const nsAString& name, const char *info)
264 if (name.IsEmpty())
265 return false;
267 const uint32_t maxSize = 256;
268 if (name.Length() > maxSize) {
269 ErrorInvalidValue("%s: identifier is %d characters long, exceeds the maximum allowed length of %d characters",
270 info, name.Length(), maxSize);
271 return false;
274 if (!ValidateGLSLString(name, info)) {
275 return false;
278 nsString prefix1 = NS_LITERAL_STRING("webgl_");
279 nsString prefix2 = NS_LITERAL_STRING("_webgl_");
281 if (Substring(name, 0, prefix1.Length()).Equals(prefix1) ||
282 Substring(name, 0, prefix2.Length()).Equals(prefix2))
284 ErrorInvalidOperation("%s: string contains a reserved GLSL prefix", info);
285 return false;
288 return true;
291 bool WebGLContext::ValidateGLSLString(const nsAString& string, const char *info)
293 for (uint32_t i = 0; i < string.Length(); ++i) {
294 if (!ValidateGLSLCharacter(string.CharAt(i))) {
295 ErrorInvalidValue("%s: string contains the illegal character '%d'", info, string.CharAt(i));
296 return false;
300 return true;
303 bool WebGLContext::ValidateTexImage2DTarget(GLenum target, GLsizei width, GLsizei height,
304 const char* info)
306 switch (target) {
307 case LOCAL_GL_TEXTURE_2D:
308 break;
309 case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X:
310 case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
311 case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
312 case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
313 case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
314 case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
315 if (width != height) {
316 ErrorInvalidValue("%s: with cube map targets, width and height must be equal", info);
317 return false;
319 break;
320 default:
321 ErrorInvalidEnum("%s: invalid target enum 0x%x", info, target);
322 return false;
325 return true;
328 bool WebGLContext::ValidateCompressedTextureSize(GLenum target, GLint level,
329 GLenum format,
330 GLsizei width, GLsizei height, uint32_t byteLength, const char* info)
332 if (!ValidateLevelWidthHeightForTarget(target, level, width, height, info)) {
333 return false;
336 // negative width and height must already have been handled above
337 MOZ_ASSERT(width >= 0 && height >= 0);
339 CheckedUint32 required_byteLength = 0;
341 switch (format) {
342 case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
343 case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
344 case LOCAL_GL_ATC_RGB:
346 required_byteLength = ((CheckedUint32(width) + 3) / 4) * ((CheckedUint32(height) + 3) / 4) * 8;
347 break;
349 case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
350 case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
351 case LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA:
352 case LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA:
354 required_byteLength = ((CheckedUint32(width) + 3) / 4) * ((CheckedUint32(height) + 3) / 4) * 16;
355 break;
357 case LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1:
358 case LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1:
360 required_byteLength = CheckedUint32(std::max(width, 8)) * CheckedUint32(std::max(height, 8)) / 2;
361 break;
363 case LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1:
364 case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1:
366 required_byteLength = CheckedUint32(std::max(width, 16)) * CheckedUint32(std::max(height, 8)) / 4;
367 break;
371 if (!required_byteLength.isValid() || required_byteLength.value() != byteLength) {
372 ErrorInvalidValue("%s: data size does not match dimensions", info);
373 return false;
376 switch (format) {
377 case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
378 case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
379 case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
380 case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
382 if (level == 0 && width % 4 == 0 && height % 4 == 0) {
383 break;
385 if (level > 0
386 && (width == 0 || width == 1 || width == 2 || width % 4 == 0)
387 && (height == 0 || height == 1 || height == 2 || height % 4 == 0))
389 break;
391 ErrorInvalidOperation("%s: level parameter does not match width and height", info);
392 return false;
394 case LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1:
395 case LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1:
396 case LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1:
397 case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1:
399 if (!is_pot_assuming_nonnegative(width) ||
400 !is_pot_assuming_nonnegative(height))
402 ErrorInvalidValue("%s: width and height must be powers of two", info);
403 return false;
408 return true;
411 bool WebGLContext::ValidateLevelWidthHeightForTarget(GLenum target, GLint level, GLsizei width,
412 GLsizei height, const char* info)
414 GLsizei maxTextureSize = MaxTextureSizeForTarget(target);
416 if (level < 0) {
417 ErrorInvalidValue("%s: level must be >= 0", info);
418 return false;
421 GLsizei maxAllowedSize = maxTextureSize >> level;
423 if (!maxAllowedSize) {
424 ErrorInvalidValue("%s: 2^level exceeds maximum texture size", info);
425 return false;
428 if (width < 0 || height < 0) {
429 ErrorInvalidValue("%s: width and height must be >= 0", info);
430 return false;
433 if (width > maxAllowedSize || height > maxAllowedSize) {
434 ErrorInvalidValue("%s: the maximum texture size for level %d is %d", info, level, maxAllowedSize);
435 return false;
438 return true;
441 uint32_t WebGLContext::GetBitsPerTexel(GLenum format, GLenum type)
443 // If there is no defined format or type, we're not taking up any memory
444 if (!format || !type) {
445 return 0;
448 if (format == LOCAL_GL_DEPTH_COMPONENT) {
449 if (type == LOCAL_GL_UNSIGNED_SHORT)
450 return 2;
451 else if (type == LOCAL_GL_UNSIGNED_INT)
452 return 4;
453 } else if (format == LOCAL_GL_DEPTH_STENCIL) {
454 if (type == LOCAL_GL_UNSIGNED_INT_24_8_EXT)
455 return 4;
458 if (type == LOCAL_GL_UNSIGNED_BYTE || type == LOCAL_GL_FLOAT) {
459 int multiplier = type == LOCAL_GL_FLOAT ? 32 : 8;
460 switch (format) {
461 case LOCAL_GL_ALPHA:
462 case LOCAL_GL_LUMINANCE:
463 return 1 * multiplier;
464 case LOCAL_GL_LUMINANCE_ALPHA:
465 return 2 * multiplier;
466 case LOCAL_GL_RGB:
467 return 3 * multiplier;
468 case LOCAL_GL_RGBA:
469 return 4 * multiplier;
470 case LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1:
471 case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1:
472 return 2;
473 case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
474 case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
475 case LOCAL_GL_ATC_RGB:
476 case LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1:
477 case LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1:
478 return 4;
479 case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
480 case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
481 case LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA:
482 case LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA:
483 return 8;
484 default:
485 break;
487 } else if (type == LOCAL_GL_UNSIGNED_SHORT_4_4_4_4 ||
488 type == LOCAL_GL_UNSIGNED_SHORT_5_5_5_1 ||
489 type == LOCAL_GL_UNSIGNED_SHORT_5_6_5)
491 return 16;
494 MOZ_ASSERT(false);
495 return 0;
498 bool WebGLContext::ValidateTexFormatAndType(GLenum format, GLenum type, int jsArrayType,
499 uint32_t *texelSize, const char *info)
501 if (IsExtensionEnabled(WEBGL_depth_texture)) {
502 if (format == LOCAL_GL_DEPTH_COMPONENT) {
503 if (jsArrayType != -1) {
504 if ((type == LOCAL_GL_UNSIGNED_SHORT && jsArrayType != js::ArrayBufferView::TYPE_UINT16) ||
505 (type == LOCAL_GL_UNSIGNED_INT && jsArrayType != js::ArrayBufferView::TYPE_UINT32)) {
506 ErrorInvalidOperation("%s: invalid typed array type for given texture data type", info);
507 return false;
511 switch(type) {
512 case LOCAL_GL_UNSIGNED_SHORT:
513 *texelSize = 2;
514 break;
515 case LOCAL_GL_UNSIGNED_INT:
516 *texelSize = 4;
517 break;
518 default:
519 ErrorInvalidOperation("%s: invalid type 0x%x", info, type);
520 return false;
523 return true;
525 } else if (format == LOCAL_GL_DEPTH_STENCIL) {
526 if (type != LOCAL_GL_UNSIGNED_INT_24_8_EXT) {
527 ErrorInvalidOperation("%s: invalid format 0x%x", info, format);
528 return false;
530 if (jsArrayType != -1) {
531 if (jsArrayType != js::ArrayBufferView::TYPE_UINT32) {
532 ErrorInvalidOperation("%s: invalid typed array type for given texture data type", info);
533 return false;
537 *texelSize = 4;
538 return true;
543 if (type == LOCAL_GL_UNSIGNED_BYTE ||
544 (IsExtensionEnabled(OES_texture_float) && type == LOCAL_GL_FLOAT))
546 if (jsArrayType != -1) {
547 if ((type == LOCAL_GL_UNSIGNED_BYTE && jsArrayType != js::ArrayBufferView::TYPE_UINT8) ||
548 (type == LOCAL_GL_FLOAT && jsArrayType != js::ArrayBufferView::TYPE_FLOAT32))
550 ErrorInvalidOperation("%s: invalid typed array type for given texture data type", info);
551 return false;
555 int texMultiplier = type == LOCAL_GL_FLOAT ? 4 : 1;
556 switch (format) {
557 case LOCAL_GL_ALPHA:
558 case LOCAL_GL_LUMINANCE:
559 *texelSize = 1 * texMultiplier;
560 return true;
561 case LOCAL_GL_LUMINANCE_ALPHA:
562 *texelSize = 2 * texMultiplier;
563 return true;
564 case LOCAL_GL_RGB:
565 *texelSize = 3 * texMultiplier;
566 return true;
567 case LOCAL_GL_RGBA:
568 *texelSize = 4 * texMultiplier;
569 return true;
570 default:
571 break;
574 ErrorInvalidEnum("%s: invalid format 0x%x", info, format);
575 return false;
578 switch (type) {
579 case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
580 case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
581 if (jsArrayType != -1 && jsArrayType != js::ArrayBufferView::TYPE_UINT16) {
582 ErrorInvalidOperation("%s: invalid typed array type for given texture data type", info);
583 return false;
586 if (format == LOCAL_GL_RGBA) {
587 *texelSize = 2;
588 return true;
590 ErrorInvalidOperation("%s: mutually incompatible format and type", info);
591 return false;
593 case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
594 if (jsArrayType != -1 && jsArrayType != js::ArrayBufferView::TYPE_UINT16) {
595 ErrorInvalidOperation("%s: invalid typed array type for given texture data type", info);
596 return false;
599 if (format == LOCAL_GL_RGB) {
600 *texelSize = 2;
601 return true;
603 ErrorInvalidOperation("%s: mutually incompatible format and type", info);
604 return false;
606 default:
607 break;
610 ErrorInvalidEnum("%s: invalid type 0x%x", info, type);
611 return false;
614 bool
615 WebGLContext::ValidateUniformLocation(const char* info, WebGLUniformLocation *location_object)
617 if (!ValidateObjectAllowNull(info, location_object))
618 return false;
619 if (!location_object)
620 return false;
621 /* the need to check specifically for !mCurrentProgram here is explained in bug 657556 */
622 if (!mCurrentProgram) {
623 ErrorInvalidOperation("%s: no program is currently bound", info);
624 return false;
626 if (mCurrentProgram != location_object->Program()) {
627 ErrorInvalidOperation("%s: this uniform location doesn't correspond to the current program", info);
628 return false;
630 if (mCurrentProgram->Generation() != location_object->ProgramGeneration()) {
631 ErrorInvalidOperation("%s: This uniform location is obsolete since the program has been relinked", info);
632 return false;
634 return true;
637 bool
638 WebGLContext::ValidateSamplerUniformSetter(const char* info, WebGLUniformLocation *location, GLint value)
640 if (location->Info().type != SH_SAMPLER_2D &&
641 location->Info().type != SH_SAMPLER_CUBE)
643 return true;
646 if (value >= 0 && value < mGLMaxTextureUnits)
647 return true;
649 ErrorInvalidValue("%s: this uniform location is a sampler, but %d is not a valid texture unit",
650 info, value);
651 return false;
654 bool
655 WebGLContext::ValidateAttribArraySetter(const char* name, uint32_t cnt, uint32_t arrayLength)
657 if (IsContextLost()) {
658 return false;
660 if (arrayLength < cnt) {
661 ErrorInvalidOperation("%s: array must be >= %d elements", name, cnt);
662 return false;
664 return true;
667 bool
668 WebGLContext::ValidateUniformArraySetter(const char* name, uint32_t expectedElemSize, WebGLUniformLocation *location_object,
669 GLint& location, uint32_t& numElementsToUpload, uint32_t arrayLength)
671 if (IsContextLost())
672 return false;
673 if (!ValidateUniformLocation(name, location_object))
674 return false;
675 location = location_object->Location();
676 uint32_t uniformElemSize = location_object->ElementSize();
677 if (expectedElemSize != uniformElemSize) {
678 ErrorInvalidOperation("%s: this function expected a uniform of element size %d,"
679 " got a uniform of element size %d", name,
680 expectedElemSize,
681 uniformElemSize);
682 return false;
684 if (arrayLength == 0 ||
685 arrayLength % expectedElemSize)
687 ErrorInvalidValue("%s: expected an array of length a multiple"
688 " of %d, got an array of length %d", name,
689 expectedElemSize,
690 arrayLength);
691 return false;
693 const WebGLUniformInfo& info = location_object->Info();
694 if (!info.isArray &&
695 arrayLength != expectedElemSize) {
696 ErrorInvalidOperation("%s: expected an array of length exactly"
697 " %d (since this uniform is not an array"
698 " uniform), got an array of length %d", name,
699 expectedElemSize,
700 arrayLength);
701 return false;
703 numElementsToUpload =
704 std::min(info.arraySize, arrayLength / expectedElemSize);
705 return true;
708 bool
709 WebGLContext::ValidateUniformMatrixArraySetter(const char* name, int dim, WebGLUniformLocation *location_object,
710 GLint& location, uint32_t& numElementsToUpload, uint32_t arrayLength,
711 WebGLboolean aTranspose)
713 uint32_t expectedElemSize = (dim)*(dim);
714 if (IsContextLost())
715 return false;
716 if (!ValidateUniformLocation(name, location_object))
717 return false;
718 location = location_object->Location();
719 uint32_t uniformElemSize = location_object->ElementSize();
720 if (expectedElemSize != uniformElemSize) {
721 ErrorInvalidOperation("%s: this function expected a uniform of element size %d,"
722 " got a uniform of element size %d", name,
723 expectedElemSize,
724 uniformElemSize);
725 return false;
727 if (arrayLength == 0 ||
728 arrayLength % expectedElemSize)
730 ErrorInvalidValue("%s: expected an array of length a multiple"
731 " of %d, got an array of length %d", name,
732 expectedElemSize,
733 arrayLength);
734 return false;
736 const WebGLUniformInfo& info = location_object->Info();
737 if (!info.isArray &&
738 arrayLength != expectedElemSize) {
739 ErrorInvalidOperation("%s: expected an array of length exactly"
740 " %d (since this uniform is not an array"
741 " uniform), got an array of length %d", name,
742 expectedElemSize,
743 arrayLength);
744 return false;
746 if (aTranspose) {
747 ErrorInvalidValue("%s: transpose must be FALSE as per the "
748 "OpenGL ES 2.0 spec", name);
749 return false;
751 numElementsToUpload =
752 std::min(info.arraySize, arrayLength / (expectedElemSize));
753 return true;
756 bool
757 WebGLContext::ValidateUniformSetter(const char* name, WebGLUniformLocation *location_object, GLint& location)
759 if (IsContextLost())
760 return false;
761 if (!ValidateUniformLocation(name, location_object))
762 return false;
763 location = location_object->Location();
764 return true;
767 bool WebGLContext::ValidateAttribIndex(GLuint index, const char *info)
769 return mBoundVertexArray->EnsureAttrib(index, info);
772 bool WebGLContext::ValidateStencilParamsForDrawCall()
774 const char *msg = "%s set different front and back stencil %s. Drawing in this configuration is not allowed.";
775 if (mStencilRefFront != mStencilRefBack) {
776 ErrorInvalidOperation(msg, "stencilFuncSeparate", "reference values");
777 return false;
779 if (mStencilValueMaskFront != mStencilValueMaskBack) {
780 ErrorInvalidOperation(msg, "stencilFuncSeparate", "value masks");
781 return false;
783 if (mStencilWriteMaskFront != mStencilWriteMaskBack) {
784 ErrorInvalidOperation(msg, "stencilMaskSeparate", "write masks");
785 return false;
787 return true;
790 static inline int32_t floorPOT(int32_t x)
792 MOZ_ASSERT(x > 0);
793 int32_t pot = 1;
794 while (pot < 0x40000000) {
795 if (x < pot*2)
796 break;
797 pot *= 2;
799 return pot;
802 bool
803 WebGLContext::InitAndValidateGL()
805 if (!gl) return false;
807 GLenum error = gl->fGetError();
808 if (error != LOCAL_GL_NO_ERROR) {
809 GenerateWarning("GL error 0x%x occurred during OpenGL context initialization, before WebGL initialization!", error);
810 return false;
813 mMinCapability = Preferences::GetBool("webgl.min_capability_mode", false);
814 mDisableExtensions = Preferences::GetBool("webgl.disable-extensions", false);
815 mLoseContextOnHeapMinimize = Preferences::GetBool("webgl.lose-context-on-heap-minimize", false);
816 mCanLoseContextInForeground = Preferences::GetBool("webgl.can-lose-context-in-foreground", true);
818 if (MinCapabilityMode()) {
819 mDisableFragHighP = true;
822 mActiveTexture = 0;
823 mWebGLError = LOCAL_GL_NO_ERROR;
825 mBound2DTextures.Clear();
826 mBoundCubeMapTextures.Clear();
828 mBoundArrayBuffer = nullptr;
829 mBoundTransformFeedbackBuffer = nullptr;
830 mCurrentProgram = nullptr;
832 mBoundFramebuffer = nullptr;
833 mBoundRenderbuffer = nullptr;
835 MakeContextCurrent();
837 // on desktop OpenGL, we always keep vertex attrib 0 array enabled
838 if (!gl->IsGLES2()) {
839 gl->fEnableVertexAttribArray(0);
842 if (MinCapabilityMode()) {
843 mGLMaxVertexAttribs = MINVALUE_GL_MAX_VERTEX_ATTRIBS;
844 } else {
845 gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_ATTRIBS, &mGLMaxVertexAttribs);
847 if (mGLMaxVertexAttribs < 8) {
848 GenerateWarning("GL_MAX_VERTEX_ATTRIBS: %d is < 8!", mGLMaxVertexAttribs);
849 return false;
852 // Note: GL_MAX_TEXTURE_UNITS is fixed at 4 for most desktop hardware,
853 // even though the hardware supports much more. The
854 // GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS value is the accurate value.
855 if (MinCapabilityMode()) {
856 mGLMaxTextureUnits = MINVALUE_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS;
857 } else {
858 gl->fGetIntegerv(LOCAL_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &mGLMaxTextureUnits);
860 if (mGLMaxTextureUnits < 8) {
861 GenerateWarning("GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: %d is < 8!", mGLMaxTextureUnits);
862 return false;
865 mBound2DTextures.SetLength(mGLMaxTextureUnits);
866 mBoundCubeMapTextures.SetLength(mGLMaxTextureUnits);
868 if (MinCapabilityMode()) {
869 mGLMaxTextureSize = MINVALUE_GL_MAX_TEXTURE_SIZE;
870 mGLMaxCubeMapTextureSize = MINVALUE_GL_MAX_CUBE_MAP_TEXTURE_SIZE;
871 mGLMaxRenderbufferSize = MINVALUE_GL_MAX_RENDERBUFFER_SIZE;
872 mGLMaxTextureImageUnits = MINVALUE_GL_MAX_TEXTURE_IMAGE_UNITS;
873 mGLMaxVertexTextureImageUnits = MINVALUE_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS;
874 } else {
875 gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, &mGLMaxTextureSize);
876 gl->fGetIntegerv(LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE, &mGLMaxCubeMapTextureSize);
877 gl->fGetIntegerv(LOCAL_GL_MAX_RENDERBUFFER_SIZE, &mGLMaxRenderbufferSize);
878 gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS, &mGLMaxTextureImageUnits);
879 gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &mGLMaxVertexTextureImageUnits);
882 mGLMaxTextureSize = floorPOT(mGLMaxTextureSize);
883 mGLMaxRenderbufferSize = floorPOT(mGLMaxRenderbufferSize);
885 if (MinCapabilityMode()) {
886 mGLMaxFragmentUniformVectors = MINVALUE_GL_MAX_FRAGMENT_UNIFORM_VECTORS;
887 mGLMaxVertexUniformVectors = MINVALUE_GL_MAX_VERTEX_UNIFORM_VECTORS;
888 mGLMaxVaryingVectors = MINVALUE_GL_MAX_VARYING_VECTORS;
889 } else {
890 if (gl->IsSupported(gl::GLFeature::ES2_compatibility)) {
891 gl->fGetIntegerv(LOCAL_GL_MAX_FRAGMENT_UNIFORM_VECTORS, &mGLMaxFragmentUniformVectors);
892 gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_UNIFORM_VECTORS, &mGLMaxVertexUniformVectors);
893 gl->fGetIntegerv(LOCAL_GL_MAX_VARYING_VECTORS, &mGLMaxVaryingVectors);
894 } else {
895 gl->fGetIntegerv(LOCAL_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &mGLMaxFragmentUniformVectors);
896 mGLMaxFragmentUniformVectors /= 4;
897 gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_UNIFORM_COMPONENTS, &mGLMaxVertexUniformVectors);
898 mGLMaxVertexUniformVectors /= 4;
900 // we are now going to try to read GL_MAX_VERTEX_OUTPUT_COMPONENTS and GL_MAX_FRAGMENT_INPUT_COMPONENTS,
901 // however these constants only entered the OpenGL standard at OpenGL 3.2. So we will try reading,
902 // and check OpenGL error for INVALID_ENUM.
904 // before we start, we check that no error already occurred, to prevent hiding it in our subsequent error handling
905 error = gl->GetAndClearError();
906 if (error != LOCAL_GL_NO_ERROR) {
907 GenerateWarning("GL error 0x%x occurred during WebGL context initialization!", error);
908 return false;
911 // On the public_webgl list, "problematic GetParameter pnames" thread, the following formula was given:
912 // mGLMaxVaryingVectors = min (GL_MAX_VERTEX_OUTPUT_COMPONENTS, GL_MAX_FRAGMENT_INPUT_COMPONENTS) / 4
913 GLint maxVertexOutputComponents,
914 minFragmentInputComponents;
915 gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_OUTPUT_COMPONENTS, &maxVertexOutputComponents);
916 gl->fGetIntegerv(LOCAL_GL_MAX_FRAGMENT_INPUT_COMPONENTS, &minFragmentInputComponents);
918 error = gl->GetAndClearError();
919 switch (error) {
920 case LOCAL_GL_NO_ERROR:
921 mGLMaxVaryingVectors = std::min(maxVertexOutputComponents, minFragmentInputComponents) / 4;
922 break;
923 case LOCAL_GL_INVALID_ENUM:
924 mGLMaxVaryingVectors = 16; // = 64/4, 64 is the min value for maxVertexOutputComponents in OpenGL 3.2 spec
925 break;
926 default:
927 GenerateWarning("GL error 0x%x occurred during WebGL context initialization!", error);
928 return false;
933 // Always 1 for GLES2
934 mMaxFramebufferColorAttachments = 1;
936 if (!gl->IsGLES2()) {
937 // gl_PointSize is always available in ES2 GLSL, but has to be
938 // specifically enabled on desktop GLSL.
939 gl->fEnable(LOCAL_GL_VERTEX_PROGRAM_POINT_SIZE);
941 // gl_PointCoord is always available in ES2 GLSL and in newer desktop GLSL versions, but apparently
942 // not in OpenGL 2 and apparently not (due to a driver bug) on certain NVIDIA setups. See:
943 // http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=261472
944 // Note that this used to cause crashes on old ATI drivers... hopefully not a significant
945 // problem anymore. See bug 602183.
946 gl->fEnable(LOCAL_GL_POINT_SPRITE);
949 #ifdef XP_MACOSX
950 if (gl->WorkAroundDriverBugs() &&
951 gl->Vendor() == gl::GLContext::VendorATI) {
952 // The Mac ATI driver, in all known OSX version up to and including 10.8,
953 // renders points sprites upside-down. Apple bug 11778921
954 gl->fPointParameterf(LOCAL_GL_POINT_SPRITE_COORD_ORIGIN, LOCAL_GL_LOWER_LEFT);
956 #endif
958 // Check the shader validator pref
959 NS_ENSURE_TRUE(Preferences::GetRootBranch(), false);
961 mShaderValidation =
962 Preferences::GetBool("webgl.shader_validator", mShaderValidation);
964 // initialize shader translator
965 if (mShaderValidation) {
966 if (!ShInitialize()) {
967 GenerateWarning("GLSL translator initialization failed!");
968 return false;
972 // Mesa can only be detected with the GL_VERSION string, of the form "2.1 Mesa 7.11.0"
973 mIsMesa = strstr((const char *)(gl->fGetString(LOCAL_GL_VERSION)), "Mesa");
975 // notice that the point of calling GetAndClearError here is not only to check for error,
976 // it is also to reset the error flags so that a subsequent WebGL getError call will give the correct result.
977 error = gl->GetAndClearError();
978 if (error != LOCAL_GL_NO_ERROR) {
979 GenerateWarning("GL error 0x%x occurred during WebGL context initialization!", error);
980 return false;
983 if (IsWebGL2() &&
984 !InitWebGL2())
986 // Todo: Bug 898404: Only allow WebGL2 on GL>=3.0 on desktop GL.
987 return false;
990 mMemoryPressureObserver
991 = new WebGLMemoryPressureObserver(this);
992 nsCOMPtr<nsIObserverService> observerService
993 = mozilla::services::GetObserverService();
994 if (observerService) {
995 observerService->AddObserver(mMemoryPressureObserver,
996 "memory-pressure",
997 false);
1000 mDefaultVertexArray = new WebGLVertexArray(this);
1001 mDefaultVertexArray->mAttribs.SetLength(mGLMaxVertexAttribs);
1002 mBoundVertexArray = mDefaultVertexArray;
1004 return true;