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/. */
5 
6 #include "WebGLProgram.h"
7 
8 #include "GLContext.h"
9 #include "mozilla/CheckedInt.h"
10 #include "mozilla/dom/WebGL2RenderingContextBinding.h"
11 #include "mozilla/dom/WebGLRenderingContextBinding.h"
12 #include "mozilla/RefPtr.h"
13 #include "nsPrintfCString.h"
14 #include "WebGLActiveInfo.h"
15 #include "WebGLBuffer.h"
16 #include "WebGLContext.h"
17 #include "WebGLShader.h"
18 #include "WebGLTransformFeedback.h"
19 #include "WebGLUniformLocation.h"
20 #include "WebGLValidateStrings.h"
21 #include "WebGLVertexArray.h"
22 
23 namespace mozilla {
24 
25 /* If `name`: "foo[3]"
26  * Then returns true, with
27  *     `out_baseName`: "foo"
28  *     `out_isArray`: true
29  *     `out_index`: 3
30  *
31  * If `name`: "foo"
32  * Then returns true, with
33  *     `out_baseName`: "foo"
34  *     `out_isArray`: false
35  *     `out_index`: 0
36  */
ParseName(const nsCString & name,nsCString * const out_baseName,bool * const out_isArray,size_t * const out_arrayIndex)37 static bool ParseName(const nsCString& name, nsCString* const out_baseName,
38                       bool* const out_isArray, size_t* const out_arrayIndex) {
39   int32_t indexEnd = name.RFind("]");
40   if (indexEnd == -1 || (uint32_t)indexEnd != name.Length() - 1) {
41     *out_baseName = name;
42     *out_isArray = false;
43     *out_arrayIndex = 0;
44     return true;
45   }
46 
47   int32_t indexOpenBracket = name.RFind("[");
48   if (indexOpenBracket == -1) return false;
49 
50   uint32_t indexStart = indexOpenBracket + 1;
51   uint32_t indexLen = indexEnd - indexStart;
52   if (indexLen == 0) return false;
53 
54   const nsAutoCString indexStr(Substring(name, indexStart, indexLen));
55 
56   nsresult errorcode;
57   int32_t indexNum = indexStr.ToInteger(&errorcode);
58   if (NS_FAILED(errorcode)) return false;
59 
60   if (indexNum < 0) return false;
61 
62   *out_baseName = StringHead(name, indexOpenBracket);
63   *out_isArray = true;
64   *out_arrayIndex = indexNum;
65   return true;
66 }
67 
AssembleName(const nsCString & baseName,bool isArray,size_t arrayIndex,nsCString * const out_name)68 static void AssembleName(const nsCString& baseName, bool isArray,
69                          size_t arrayIndex, nsCString* const out_name) {
70   *out_name = baseName;
71   if (isArray) {
72     out_name->Append('[');
73     out_name->AppendInt(uint64_t(arrayIndex));
74     out_name->Append(']');
75   }
76 }
77 
78 ////
79 
AttribBaseType(GLenum attribType)80 static GLenum AttribBaseType(GLenum attribType) {
81   switch (attribType) {
82     case LOCAL_GL_FLOAT:
83     case LOCAL_GL_FLOAT_VEC2:
84     case LOCAL_GL_FLOAT_VEC3:
85     case LOCAL_GL_FLOAT_VEC4:
86 
87     case LOCAL_GL_FLOAT_MAT2:
88     case LOCAL_GL_FLOAT_MAT2x3:
89     case LOCAL_GL_FLOAT_MAT2x4:
90 
91     case LOCAL_GL_FLOAT_MAT3x2:
92     case LOCAL_GL_FLOAT_MAT3:
93     case LOCAL_GL_FLOAT_MAT3x4:
94 
95     case LOCAL_GL_FLOAT_MAT4x2:
96     case LOCAL_GL_FLOAT_MAT4x3:
97     case LOCAL_GL_FLOAT_MAT4:
98       return LOCAL_GL_FLOAT;
99 
100     case LOCAL_GL_INT:
101     case LOCAL_GL_INT_VEC2:
102     case LOCAL_GL_INT_VEC3:
103     case LOCAL_GL_INT_VEC4:
104       return LOCAL_GL_INT;
105 
106     case LOCAL_GL_UNSIGNED_INT:
107     case LOCAL_GL_UNSIGNED_INT_VEC2:
108     case LOCAL_GL_UNSIGNED_INT_VEC3:
109     case LOCAL_GL_UNSIGNED_INT_VEC4:
110       return LOCAL_GL_UNSIGNED_INT;
111 
112     default:
113       MOZ_ASSERT(false, "unexpected attrib elemType");
114       return 0;
115   }
116 }
117 
118 ////
119 
GetTexList(WebGLActiveInfo * activeInfo)120 /*static*/ const webgl::UniformInfo::TexListT* webgl::UniformInfo::GetTexList(
121     WebGLActiveInfo* activeInfo) {
122   const auto& webgl = activeInfo->mWebGL;
123 
124   switch (activeInfo->mElemType) {
125     case LOCAL_GL_SAMPLER_2D:
126     case LOCAL_GL_SAMPLER_2D_SHADOW:
127     case LOCAL_GL_INT_SAMPLER_2D:
128     case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
129       return &webgl->mBound2DTextures;
130 
131     case LOCAL_GL_SAMPLER_CUBE:
132     case LOCAL_GL_SAMPLER_CUBE_SHADOW:
133     case LOCAL_GL_INT_SAMPLER_CUBE:
134     case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
135       return &webgl->mBoundCubeMapTextures;
136 
137     case LOCAL_GL_SAMPLER_3D:
138     case LOCAL_GL_INT_SAMPLER_3D:
139     case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
140       return &webgl->mBound3DTextures;
141 
142     case LOCAL_GL_SAMPLER_2D_ARRAY:
143     case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
144     case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
145     case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
146       return &webgl->mBound2DArrayTextures;
147 
148     default:
149       return nullptr;
150   }
151 }
152 
UniformInfo(WebGLActiveInfo * activeInfo)153 webgl::UniformInfo::UniformInfo(WebGLActiveInfo* activeInfo)
154     : mActiveInfo(activeInfo), mSamplerTexList(GetTexList(activeInfo)) {
155   if (mSamplerTexList) {
156     mSamplerValues.assign(mActiveInfo->mElemCount, 0);
157   }
158 }
159 
160 //////////
161 
162 //#define DUMP_SHADERVAR_MAPPINGS
163 
QueryProgramInfo(WebGLProgram * prog,gl::GLContext * gl)164 static already_AddRefed<const webgl::LinkedProgramInfo> QueryProgramInfo(
165     WebGLProgram* prog, gl::GLContext* gl) {
166   WebGLContext* const webgl = prog->mContext;
167 
168   RefPtr<webgl::LinkedProgramInfo> info(new webgl::LinkedProgramInfo(prog));
169 
170   GLuint maxAttribLenWithNull = 0;
171   gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_ATTRIBUTE_MAX_LENGTH,
172                     (GLint*)&maxAttribLenWithNull);
173   if (maxAttribLenWithNull < 1) maxAttribLenWithNull = 1;
174 
175   GLuint maxUniformLenWithNull = 0;
176   gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_UNIFORM_MAX_LENGTH,
177                     (GLint*)&maxUniformLenWithNull);
178   if (maxUniformLenWithNull < 1) maxUniformLenWithNull = 1;
179 
180   GLuint maxUniformBlockLenWithNull = 0;
181   if (gl->IsSupported(gl::GLFeature::uniform_buffer_object)) {
182     gl->fGetProgramiv(prog->mGLName,
183                       LOCAL_GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH,
184                       (GLint*)&maxUniformBlockLenWithNull);
185     if (maxUniformBlockLenWithNull < 1) maxUniformBlockLenWithNull = 1;
186   }
187 
188   GLuint maxTransformFeedbackVaryingLenWithNull = 0;
189   if (gl->IsSupported(gl::GLFeature::transform_feedback2)) {
190     gl->fGetProgramiv(prog->mGLName,
191                       LOCAL_GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH,
192                       (GLint*)&maxTransformFeedbackVaryingLenWithNull);
193     if (maxTransformFeedbackVaryingLenWithNull < 1)
194       maxTransformFeedbackVaryingLenWithNull = 1;
195   }
196 
197   // Attribs (can't be arrays)
198 
199   GLuint numActiveAttribs = 0;
200   gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_ATTRIBUTES,
201                     (GLint*)&numActiveAttribs);
202 
203   for (GLuint i = 0; i < numActiveAttribs; i++) {
204     nsAutoCString mappedName;
205     mappedName.SetLength(maxAttribLenWithNull - 1);
206 
207     GLsizei lengthWithoutNull = 0;
208     GLint elemCount = 0;  // `size`
209     GLenum elemType = 0;  // `type`
210     gl->fGetActiveAttrib(prog->mGLName, i, mappedName.Length() + 1,
211                          &lengthWithoutNull, &elemCount, &elemType,
212                          mappedName.BeginWriting());
213     GLenum error = gl->fGetError();
214     if (error != LOCAL_GL_NO_ERROR) {
215       gfxCriticalNote << "Failed to do glGetActiveAttrib: " << error;
216     }
217 
218     mappedName.SetLength(lengthWithoutNull);
219 
220     ////
221 
222     nsCString userName;
223     if (!prog->FindAttribUserNameByMappedName(mappedName, &userName)) {
224       userName = mappedName;
225     }
226 
227     ///////
228 
229     GLint loc =
230         gl->fGetAttribLocation(prog->mGLName, mappedName.BeginReading());
231     if (gl->WorkAroundDriverBugs() && mappedName.EqualsIgnoreCase("gl_", 3)) {
232       // Bug 1328559: Appears problematic on ANGLE and OSX, but not Linux or
233       // Win+GL.
234       loc = -1;
235     }
236 #ifdef DUMP_SHADERVAR_MAPPINGS
237     printf_stderr("[attrib %u/%u] @%i %s->%s\n", i, numActiveAttribs, loc,
238                   userName.BeginReading(), mappedName.BeginReading());
239 #endif
240     MOZ_ASSERT_IF(mappedName.EqualsIgnoreCase("gl_", 3), loc == -1);
241 
242     ///////
243 
244     const bool isArray = false;
245     const RefPtr<WebGLActiveInfo> activeInfo = new WebGLActiveInfo(
246         webgl, elemCount, elemType, isArray, userName, mappedName);
247     const GLenum baseType = AttribBaseType(elemType);
248     const webgl::AttribInfo attrib = {activeInfo, loc, baseType};
249     info->attribs.push_back(attrib);
250 
251     if (loc == 0) {
252       info->attrib0Active = true;
253     }
254   }
255 
256   // Uniforms (can be basically anything)
257 
258   const bool needsCheckForArrays = gl->WorkAroundDriverBugs();
259 
260   GLuint numActiveUniforms = 0;
261   gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_UNIFORMS,
262                     (GLint*)&numActiveUniforms);
263 
264   for (GLuint i = 0; i < numActiveUniforms; i++) {
265     nsAutoCString mappedName;
266     mappedName.SetLength(maxUniformLenWithNull - 1);
267 
268     GLsizei lengthWithoutNull = 0;
269     GLint elemCount = 0;  // `size`
270     GLenum elemType = 0;  // `type`
271     gl->fGetActiveUniform(prog->mGLName, i, mappedName.Length() + 1,
272                           &lengthWithoutNull, &elemCount, &elemType,
273                           mappedName.BeginWriting());
274 
275     mappedName.SetLength(lengthWithoutNull);
276 
277     ///////
278 
279     nsAutoCString baseMappedName;
280     bool isArray;
281     size_t arrayIndex;
282     if (!ParseName(mappedName, &baseMappedName, &isArray, &arrayIndex))
283       MOZ_CRASH("GFX: Failed to parse `mappedName` received from driver.");
284 
285     // Note that for good drivers, `isArray` should already be correct.
286     // However, if FindUniform succeeds, it will be validator-guaranteed
287     // correct.
288 
289     ///////
290 
291     nsAutoCString baseUserName;
292     if (!prog->FindUniformByMappedName(baseMappedName, &baseUserName,
293                                        &isArray)) {
294       // Validator likely missing.
295       baseUserName = baseMappedName;
296 
297       if (needsCheckForArrays && !isArray) {
298         // By GLES 3, GetUniformLocation("foo[0]") should return -1 if `foo` is
299         // not an array. Our current linux Try slaves return the location of
300         // `foo` anyways, though.
301         std::string mappedNameStr = baseMappedName.BeginReading();
302         mappedNameStr += "[0]";
303 
304         GLint loc =
305             gl->fGetUniformLocation(prog->mGLName, mappedNameStr.c_str());
306         if (loc != -1) isArray = true;
307       }
308     }
309 
310       ///////
311 
312 #ifdef DUMP_SHADERVAR_MAPPINGS
313     printf_stderr("[uniform %u/%u] %s->%s\n", i, numActiveUniforms,
314                   baseUserName.BeginReading(), mappedName.BeginReading());
315 #endif
316 
317     ///////
318 
319     const RefPtr<WebGLActiveInfo> activeInfo = new WebGLActiveInfo(
320         webgl, elemCount, elemType, isArray, baseUserName, baseMappedName);
321 
322     auto* uniform = new webgl::UniformInfo(activeInfo);
323     info->uniforms.push_back(uniform);
324 
325     if (uniform->mSamplerTexList) {
326       info->uniformSamplers.push_back(uniform);
327     }
328   }
329 
330   // Uniform Blocks (can be arrays, but can't contain sampler types)
331 
332   if (gl->IsSupported(gl::GLFeature::uniform_buffer_object)) {
333     GLuint numActiveUniformBlocks = 0;
334     gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_UNIFORM_BLOCKS,
335                       (GLint*)&numActiveUniformBlocks);
336 
337     for (GLuint i = 0; i < numActiveUniformBlocks; i++) {
338       nsAutoCString mappedName;
339       mappedName.SetLength(maxUniformBlockLenWithNull - 1);
340 
341       GLint lengthWithoutNull;
342       gl->fGetActiveUniformBlockiv(prog->mGLName, i,
343                                    LOCAL_GL_UNIFORM_BLOCK_NAME_LENGTH,
344                                    &lengthWithoutNull);
345       gl->fGetActiveUniformBlockName(
346           prog->mGLName, i, maxUniformBlockLenWithNull, &lengthWithoutNull,
347           mappedName.BeginWriting());
348       mappedName.SetLength(lengthWithoutNull);
349 
350       ////
351 
352       nsCString userName;
353       if (!prog->UnmapUniformBlockName(mappedName, &userName)) continue;
354 
355 #ifdef DUMP_SHADERVAR_MAPPINGS
356       printf_stderr("[uniform block %u/%u] %s->%s\n", i, numActiveUniformBlocks,
357                     userName.BeginReading(), mappedName.BeginReading());
358 #endif
359 
360       ////
361 
362       GLuint dataSize = 0;
363       gl->fGetActiveUniformBlockiv(prog->mGLName, i,
364                                    LOCAL_GL_UNIFORM_BLOCK_DATA_SIZE,
365                                    (GLint*)&dataSize);
366 
367       auto* block =
368           new webgl::UniformBlockInfo(webgl, userName, mappedName, dataSize);
369       info->uniformBlocks.push_back(block);
370     }
371   }
372 
373   // Transform feedback varyings (can be arrays)
374 
375   if (gl->IsSupported(gl::GLFeature::transform_feedback2)) {
376     GLuint numTransformFeedbackVaryings = 0;
377     gl->fGetProgramiv(prog->mGLName, LOCAL_GL_TRANSFORM_FEEDBACK_VARYINGS,
378                       (GLint*)&numTransformFeedbackVaryings);
379 
380     for (GLuint i = 0; i < numTransformFeedbackVaryings; i++) {
381       nsAutoCString mappedName;
382       mappedName.SetLength(maxTransformFeedbackVaryingLenWithNull - 1);
383 
384       GLint lengthWithoutNull;
385       GLsizei elemCount;
386       GLenum elemType;
387       gl->fGetTransformFeedbackVarying(
388           prog->mGLName, i, maxTransformFeedbackVaryingLenWithNull,
389           &lengthWithoutNull, &elemCount, &elemType, mappedName.BeginWriting());
390       mappedName.SetLength(lengthWithoutNull);
391 
392       ////
393 
394       nsAutoCString baseMappedName;
395       bool isArray;
396       size_t arrayIndex;
397       if (!ParseName(mappedName, &baseMappedName, &isArray, &arrayIndex))
398         MOZ_CRASH("GFX: Failed to parse `mappedName` received from driver.");
399 
400       nsAutoCString baseUserName;
401       if (!prog->FindVaryingByMappedName(mappedName, &baseUserName, &isArray)) {
402         baseUserName = baseMappedName;
403       }
404 
405         ////
406 
407 #ifdef DUMP_SHADERVAR_MAPPINGS
408       printf_stderr("[transform feedback varying %u/%u] %s->%s\n", i,
409                     numTransformFeedbackVaryings, baseUserName.BeginReading(),
410                     mappedName.BeginReading());
411 #endif
412 
413       const RefPtr<WebGLActiveInfo> activeInfo = new WebGLActiveInfo(
414           webgl, elemCount, elemType, isArray, baseUserName, mappedName);
415       info->transformFeedbackVaryings.push_back(activeInfo);
416     }
417   }
418 
419   // Frag outputs
420 
421   prog->EnumerateFragOutputs(info->fragDataMap);
422 
423   return info.forget();
424 }
425 
426 ////////////////////////////////////////////////////////////////////////////////
427 
LinkedProgramInfo(WebGLProgram * prog)428 webgl::LinkedProgramInfo::LinkedProgramInfo(WebGLProgram* prog)
429     : prog(prog),
430       transformFeedbackBufferMode(prog->mNextLink_TransformFeedbackBufferMode),
431       attrib0Active(false) {}
432 
~LinkedProgramInfo()433 webgl::LinkedProgramInfo::~LinkedProgramInfo() {
434   for (auto& cur : uniforms) {
435     delete cur;
436   }
437   for (auto& cur : uniformBlocks) {
438     delete cur;
439   }
440 }
441 
442 const webgl::CachedDrawFetchLimits*
GetDrawFetchLimits(const char * const funcName) const443 webgl::LinkedProgramInfo::GetDrawFetchLimits(const char* const funcName) const {
444   const auto& webgl = prog->mContext;
445   const auto& vao = webgl->mBoundVertexArray;
446 
447   const auto found = mDrawFetchCache.Find(vao);
448   if (found) return found;
449 
450   std::vector<const CacheMapInvalidator*> cacheDeps;
451   cacheDeps.push_back(vao.get());
452   cacheDeps.push_back(&webgl->mGenericVertexAttribTypeInvalidator);
453 
454   {
455     // We have to ensure that every enabled attrib array (not just the active
456     // ones) has a non-null buffer.
457     uint32_t i = 0;
458     for (const auto& cur : vao->mAttribs) {
459       if (cur.mEnabled && !cur.mBuf) {
460         webgl->ErrorInvalidOperation(
461             "%s: Vertex attrib array %u is enabled but"
462             " has no buffer bound.",
463             funcName, i);
464         return nullptr;
465       }
466     }
467   }
468 
469   bool hasActiveAttrib = false;
470   bool hasActiveDivisor0 = false;
471   webgl::CachedDrawFetchLimits fetchLimits = {UINT64_MAX, UINT64_MAX};
472 
473   for (const auto& progAttrib : this->attribs) {
474     const auto& loc = progAttrib.mLoc;
475     if (loc == -1) continue;
476     hasActiveAttrib |= true;
477 
478     const auto& attribData = vao->mAttribs[loc];
479     hasActiveDivisor0 |= (attribData.mDivisor == 0);
480 
481     GLenum attribDataBaseType;
482     if (attribData.mEnabled) {
483       MOZ_ASSERT(attribData.mBuf);
484       if (attribData.mBuf->IsBoundForTF()) {
485         webgl->ErrorInvalidOperation(
486             "%s: Vertex attrib %u's buffer is bound for"
487             " transform feedback.",
488             funcName, loc);
489         return nullptr;
490       }
491       cacheDeps.push_back(&attribData.mBuf->mFetchInvalidator);
492 
493       attribDataBaseType = attribData.BaseType();
494 
495       const size_t availBytes = attribData.mBuf->ByteLength();
496       const auto availElems =
497           AvailGroups(availBytes, attribData.ByteOffset(),
498                       attribData.BytesPerVertex(), attribData.ExplicitStride());
499       if (attribData.mDivisor) {
500         const auto availInstances =
501             CheckedInt<uint64_t>(availElems) * attribData.mDivisor;
502         if (availInstances.isValid()) {
503           fetchLimits.maxInstances =
504               std::min(fetchLimits.maxInstances, availInstances.value());
505         }  // If not valid, it overflowed too large, so we're super safe.
506       } else {
507         fetchLimits.maxVerts = std::min(fetchLimits.maxVerts, availElems);
508       }
509     } else {
510       attribDataBaseType = webgl->mGenericVertexAttribTypes[loc];
511     }
512 
513     if (attribDataBaseType != progAttrib.mBaseType) {
514       nsCString progType, dataType;
515       WebGLContext::EnumName(progAttrib.mBaseType, &progType);
516       WebGLContext::EnumName(attribDataBaseType, &dataType);
517       webgl->ErrorInvalidOperation(
518           "%s: Vertex attrib %u requires data of type %s,"
519           " but is being supplied with type %s.",
520           funcName, loc, progType.BeginReading(), dataType.BeginReading());
521       return nullptr;
522     }
523   }
524 
525   if (hasActiveAttrib && !hasActiveDivisor0) {
526     webgl->ErrorInvalidOperation(
527         "%s: One active vertex attrib (if any are active)"
528         " must have a divisor of 0.",
529         funcName);
530     return nullptr;
531   }
532 
533   // --
534 
535   return mDrawFetchCache.Insert(vao.get(), Move(fetchLimits), Move(cacheDeps));
536 }
537 
538 ////////////////////////////////////////////////////////////////////////////////
539 // WebGLProgram
540 
WebGLProgram(WebGLContext * webgl)541 WebGLProgram::WebGLProgram(WebGLContext* webgl)
542     : WebGLRefCountedObject(webgl),
543       mGLName(webgl->gl->fCreateProgram()),
544       mNumActiveTFOs(0),
545       mNextLink_TransformFeedbackBufferMode(LOCAL_GL_INTERLEAVED_ATTRIBS) {
546   mContext->mPrograms.insertBack(this);
547 }
548 
~WebGLProgram()549 WebGLProgram::~WebGLProgram() { DeleteOnce(); }
550 
Delete()551 void WebGLProgram::Delete() {
552   gl::GLContext* gl = mContext->GL();
553   gl->fDeleteProgram(mGLName);
554 
555   mVertShader = nullptr;
556   mFragShader = nullptr;
557 
558   mMostRecentLinkInfo = nullptr;
559 
560   LinkedListElement<WebGLProgram>::removeFrom(mContext->mPrograms);
561 }
562 
563 ////////////////////////////////////////////////////////////////////////////////
564 // GL funcs
565 
AttachShader(WebGLShader * shader)566 void WebGLProgram::AttachShader(WebGLShader* shader) {
567   WebGLRefPtr<WebGLShader>* shaderSlot;
568   switch (shader->mType) {
569     case LOCAL_GL_VERTEX_SHADER:
570       shaderSlot = &mVertShader;
571       break;
572     case LOCAL_GL_FRAGMENT_SHADER:
573       shaderSlot = &mFragShader;
574       break;
575     default:
576       mContext->ErrorInvalidOperation("attachShader: Bad type for shader.");
577       return;
578   }
579 
580   if (*shaderSlot) {
581     if (shader == *shaderSlot) {
582       mContext->ErrorInvalidOperation(
583           "attachShader: `shader` is already attached.");
584     } else {
585       mContext->ErrorInvalidOperation(
586           "attachShader: Only one of each type of"
587           " shader may be attached to a program.");
588     }
589     return;
590   }
591 
592   *shaderSlot = shader;
593 
594   mContext->gl->fAttachShader(mGLName, shader->mGLName);
595 }
596 
BindAttribLocation(GLuint loc,const nsAString & name)597 void WebGLProgram::BindAttribLocation(GLuint loc, const nsAString& name) {
598   if (!ValidateGLSLVariableName(name, mContext, "bindAttribLocation")) return;
599 
600   if (loc >= mContext->MaxVertexAttribs()) {
601     mContext->ErrorInvalidValue(
602         "bindAttribLocation: `location` must be less than"
603         " MAX_VERTEX_ATTRIBS.");
604     return;
605   }
606 
607   if (StringBeginsWith(name, NS_LITERAL_STRING("gl_"))) {
608     mContext->ErrorInvalidOperation(
609         "bindAttribLocation: Can't set the location of a"
610         " name that starts with 'gl_'.");
611     return;
612   }
613 
614   NS_LossyConvertUTF16toASCII asciiName(name);
615 
616   auto res = mNextLink_BoundAttribLocs.insert({asciiName, loc});
617 
618   const bool wasInserted = res.second;
619   if (!wasInserted) {
620     auto itr = res.first;
621     itr->second = loc;
622   }
623 }
624 
DetachShader(const WebGLShader * shader)625 void WebGLProgram::DetachShader(const WebGLShader* shader) {
626   MOZ_ASSERT(shader);
627 
628   WebGLRefPtr<WebGLShader>* shaderSlot;
629   switch (shader->mType) {
630     case LOCAL_GL_VERTEX_SHADER:
631       shaderSlot = &mVertShader;
632       break;
633     case LOCAL_GL_FRAGMENT_SHADER:
634       shaderSlot = &mFragShader;
635       break;
636     default:
637       mContext->ErrorInvalidOperation("attachShader: Bad type for shader.");
638       return;
639   }
640 
641   if (*shaderSlot != shader) {
642     mContext->ErrorInvalidOperation("detachShader: `shader` is not attached.");
643     return;
644   }
645 
646   *shaderSlot = nullptr;
647 
648   mContext->gl->fDetachShader(mGLName, shader->mGLName);
649 }
650 
GetActiveAttrib(GLuint index) const651 already_AddRefed<WebGLActiveInfo> WebGLProgram::GetActiveAttrib(
652     GLuint index) const {
653   if (!mMostRecentLinkInfo) {
654     RefPtr<WebGLActiveInfo> ret = WebGLActiveInfo::CreateInvalid(mContext);
655     return ret.forget();
656   }
657 
658   const auto& attribs = mMostRecentLinkInfo->attribs;
659 
660   if (index >= attribs.size()) {
661     mContext->ErrorInvalidValue("`index` (%i) must be less than %s (%zu).",
662                                 index, "ACTIVE_ATTRIBS", attribs.size());
663     return nullptr;
664   }
665 
666   RefPtr<WebGLActiveInfo> ret = attribs[index].mActiveInfo;
667   return ret.forget();
668 }
669 
GetActiveUniform(GLuint index) const670 already_AddRefed<WebGLActiveInfo> WebGLProgram::GetActiveUniform(
671     GLuint index) const {
672   if (!mMostRecentLinkInfo) {
673     // According to the spec, this can return null.
674     RefPtr<WebGLActiveInfo> ret = WebGLActiveInfo::CreateInvalid(mContext);
675     return ret.forget();
676   }
677 
678   const auto& uniforms = mMostRecentLinkInfo->uniforms;
679 
680   if (index >= uniforms.size()) {
681     mContext->ErrorInvalidValue("`index` (%i) must be less than %s (%zu).",
682                                 index, "ACTIVE_UNIFORMS", uniforms.size());
683     return nullptr;
684   }
685 
686   RefPtr<WebGLActiveInfo> ret = uniforms[index]->mActiveInfo;
687   return ret.forget();
688 }
689 
GetAttachedShaders(nsTArray<RefPtr<WebGLShader>> * const out) const690 void WebGLProgram::GetAttachedShaders(
691     nsTArray<RefPtr<WebGLShader>>* const out) const {
692   out->TruncateLength(0);
693 
694   if (mVertShader) out->AppendElement(mVertShader);
695 
696   if (mFragShader) out->AppendElement(mFragShader);
697 }
698 
GetAttribLocation(const nsAString & userName_wide) const699 GLint WebGLProgram::GetAttribLocation(const nsAString& userName_wide) const {
700   if (!ValidateGLSLVariableName(userName_wide, mContext, "getAttribLocation"))
701     return -1;
702 
703   if (!IsLinked()) {
704     mContext->ErrorInvalidOperation(
705         "getAttribLocation: `program` must be linked.");
706     return -1;
707   }
708 
709   const NS_LossyConvertUTF16toASCII userName(userName_wide);
710 
711   const webgl::AttribInfo* info;
712   if (!LinkInfo()->FindAttrib(userName, &info)) return -1;
713 
714   return GLint(info->mLoc);
715 }
716 
GetFragDataByUserName(const WebGLProgram * prog,const nsCString & userName)717 static GLint GetFragDataByUserName(const WebGLProgram* prog,
718                                    const nsCString& userName) {
719   nsCString mappedName;
720   if (!prog->LinkInfo()->MapFragDataName(userName, &mappedName)) return -1;
721 
722   return prog->mContext->gl->fGetFragDataLocation(prog->mGLName,
723                                                   mappedName.BeginReading());
724 }
725 
GetFragDataLocation(const nsAString & userName_wide) const726 GLint WebGLProgram::GetFragDataLocation(const nsAString& userName_wide) const {
727   if (!ValidateGLSLVariableName(userName_wide, mContext, "getFragDataLocation"))
728     return -1;
729 
730   if (!IsLinked()) {
731     mContext->ErrorInvalidOperation(
732         "getFragDataLocation: `program` must be linked.");
733     return -1;
734   }
735 
736   const NS_LossyConvertUTF16toASCII userName(userName_wide);
737 #ifdef XP_MACOSX
738   const auto& gl = mContext->gl;
739   if (gl->WorkAroundDriverBugs()) {
740     // OSX doesn't return locs for indexed names, just the base names.
741     // Indicated by failure in:
742     // conformance2/programs/gl-get-frag-data-location.html
743     bool isArray;
744     size_t arrayIndex;
745     nsCString baseUserName;
746     if (!ParseName(userName, &baseUserName, &isArray, &arrayIndex)) return -1;
747 
748     if (arrayIndex >= mContext->mGLMaxDrawBuffers) return -1;
749 
750     const auto baseLoc = GetFragDataByUserName(this, baseUserName);
751     const auto loc = baseLoc + GLint(arrayIndex);
752     return loc;
753   }
754 #endif
755   return GetFragDataByUserName(this, userName);
756 }
757 
GetProgramInfoLog(nsAString * const out) const758 void WebGLProgram::GetProgramInfoLog(nsAString* const out) const {
759   CopyASCIItoUTF16(mLinkLog, *out);
760 }
761 
GetProgramiv(gl::GLContext * gl,GLuint program,GLenum pname)762 static GLint GetProgramiv(gl::GLContext* gl, GLuint program, GLenum pname) {
763   GLint ret = 0;
764   gl->fGetProgramiv(program, pname, &ret);
765   return ret;
766 }
767 
GetProgramParameter(GLenum pname) const768 JS::Value WebGLProgram::GetProgramParameter(GLenum pname) const {
769   gl::GLContext* gl = mContext->gl;
770 
771   if (mContext->IsWebGL2()) {
772     switch (pname) {
773       case LOCAL_GL_ACTIVE_UNIFORM_BLOCKS:
774         if (!IsLinked()) return JS::NumberValue(0);
775         return JS::NumberValue(LinkInfo()->uniformBlocks.size());
776 
777       case LOCAL_GL_TRANSFORM_FEEDBACK_VARYINGS:
778         if (!IsLinked()) return JS::NumberValue(0);
779         return JS::NumberValue(LinkInfo()->transformFeedbackVaryings.size());
780 
781       case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_MODE:
782         if (!IsLinked()) return JS::NumberValue(LOCAL_GL_INTERLEAVED_ATTRIBS);
783         return JS::NumberValue(LinkInfo()->transformFeedbackBufferMode);
784     }
785   }
786 
787   switch (pname) {
788     case LOCAL_GL_ATTACHED_SHADERS:
789       return JS::NumberValue(int(bool(mVertShader.get())) +
790                              int(bool(mFragShader)));
791 
792     case LOCAL_GL_ACTIVE_UNIFORMS:
793       if (!IsLinked()) return JS::NumberValue(0);
794       return JS::NumberValue(LinkInfo()->uniforms.size());
795 
796     case LOCAL_GL_ACTIVE_ATTRIBUTES:
797       if (!IsLinked()) return JS::NumberValue(0);
798       return JS::NumberValue(LinkInfo()->attribs.size());
799 
800     case LOCAL_GL_DELETE_STATUS:
801       return JS::BooleanValue(IsDeleteRequested());
802 
803     case LOCAL_GL_LINK_STATUS:
804       return JS::BooleanValue(IsLinked());
805 
806     case LOCAL_GL_VALIDATE_STATUS:
807 #ifdef XP_MACOSX
808       // See comment in ValidateProgram.
809       if (gl->WorkAroundDriverBugs()) return JS::BooleanValue(true);
810 #endif
811       // Todo: Implement this in our code.
812       return JS::BooleanValue(bool(GetProgramiv(gl, mGLName, pname)));
813 
814     default:
815       mContext->ErrorInvalidEnumInfo("getProgramParameter: `pname`", pname);
816       return JS::NullValue();
817   }
818 }
819 
GetUniformBlockIndex(const nsAString & userName_wide) const820 GLuint WebGLProgram::GetUniformBlockIndex(
821     const nsAString& userName_wide) const {
822   if (!ValidateGLSLVariableName(userName_wide, mContext,
823                                 "getUniformBlockIndex"))
824     return LOCAL_GL_INVALID_INDEX;
825 
826   if (!IsLinked()) {
827     mContext->ErrorInvalidOperation(
828         "getUniformBlockIndex: `program` must be linked.");
829     return LOCAL_GL_INVALID_INDEX;
830   }
831 
832   const NS_LossyConvertUTF16toASCII userName(userName_wide);
833 
834   const webgl::UniformBlockInfo* info = nullptr;
835   for (const auto& cur : LinkInfo()->uniformBlocks) {
836     if (cur->mUserName == userName) {
837       info = cur;
838       break;
839     }
840   }
841   if (!info) return LOCAL_GL_INVALID_INDEX;
842 
843   const auto& mappedName = info->mMappedName;
844 
845   gl::GLContext* gl = mContext->GL();
846   return gl->fGetUniformBlockIndex(mGLName, mappedName.BeginReading());
847 }
848 
GetActiveUniformBlockName(GLuint uniformBlockIndex,nsAString & retval) const849 void WebGLProgram::GetActiveUniformBlockName(GLuint uniformBlockIndex,
850                                              nsAString& retval) const {
851   if (!IsLinked()) {
852     mContext->ErrorInvalidOperation(
853         "getActiveUniformBlockName: `program` must be linked.");
854     return;
855   }
856 
857   const webgl::LinkedProgramInfo* linkInfo = LinkInfo();
858   GLuint uniformBlockCount = (GLuint)linkInfo->uniformBlocks.size();
859   if (uniformBlockIndex >= uniformBlockCount) {
860     mContext->ErrorInvalidValue("getActiveUniformBlockName: index %u invalid.",
861                                 uniformBlockIndex);
862     return;
863   }
864 
865   const auto& blockInfo = linkInfo->uniformBlocks[uniformBlockIndex];
866   retval.Assign(NS_ConvertASCIItoUTF16(blockInfo->mUserName));
867 }
868 
GetActiveUniformBlockParam(GLuint uniformBlockIndex,GLenum pname) const869 JS::Value WebGLProgram::GetActiveUniformBlockParam(GLuint uniformBlockIndex,
870                                                    GLenum pname) const {
871   if (!IsLinked()) {
872     mContext->ErrorInvalidOperation(
873         "getActiveUniformBlockParameter: `program` must be linked.");
874     return JS::NullValue();
875   }
876 
877   const webgl::LinkedProgramInfo* linkInfo = LinkInfo();
878   GLuint uniformBlockCount = (GLuint)linkInfo->uniformBlocks.size();
879   if (uniformBlockIndex >= uniformBlockCount) {
880     mContext->ErrorInvalidValue(
881         "getActiveUniformBlockParameter: index %u invalid.", uniformBlockIndex);
882     return JS::NullValue();
883   }
884 
885   gl::GLContext* gl = mContext->GL();
886   GLint param = 0;
887 
888   switch (pname) {
889     case LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER:
890     case LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER:
891       gl->fGetActiveUniformBlockiv(mGLName, uniformBlockIndex, pname, &param);
892       return JS::BooleanValue(bool(param));
893 
894     case LOCAL_GL_UNIFORM_BLOCK_BINDING:
895     case LOCAL_GL_UNIFORM_BLOCK_DATA_SIZE:
896     case LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS:
897       gl->fGetActiveUniformBlockiv(mGLName, uniformBlockIndex, pname, &param);
898       return JS::NumberValue(param);
899 
900     default:
901       MOZ_CRASH("bad `pname`.");
902   }
903 }
904 
GetActiveUniformBlockActiveUniforms(JSContext * cx,GLuint uniformBlockIndex,ErrorResult * const out_error) const905 JS::Value WebGLProgram::GetActiveUniformBlockActiveUniforms(
906     JSContext* cx, GLuint uniformBlockIndex,
907     ErrorResult* const out_error) const {
908   const char funcName[] = "getActiveUniformBlockParameter";
909   if (!IsLinked()) {
910     mContext->ErrorInvalidOperation("%s: `program` must be linked.", funcName);
911     return JS::NullValue();
912   }
913 
914   const webgl::LinkedProgramInfo* linkInfo = LinkInfo();
915   GLuint uniformBlockCount = (GLuint)linkInfo->uniformBlocks.size();
916   if (uniformBlockIndex >= uniformBlockCount) {
917     mContext->ErrorInvalidValue("%s: Index %u invalid.", funcName,
918                                 uniformBlockIndex);
919     return JS::NullValue();
920   }
921 
922   gl::GLContext* gl = mContext->GL();
923   GLint activeUniformCount = 0;
924   gl->fGetActiveUniformBlockiv(mGLName, uniformBlockIndex,
925                                LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS,
926                                &activeUniformCount);
927   JS::RootedObject obj(
928       cx, dom::Uint32Array::Create(cx, mContext, activeUniformCount, nullptr));
929   if (!obj) {
930     *out_error = NS_ERROR_OUT_OF_MEMORY;
931     return JS::NullValue();
932   }
933 
934   dom::Uint32Array result;
935   DebugOnly<bool> inited = result.Init(obj);
936   MOZ_ASSERT(inited);
937   result.ComputeLengthAndData();
938   gl->fGetActiveUniformBlockiv(mGLName, uniformBlockIndex,
939                                LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES,
940                                (GLint*)result.Data());
941 
942   return JS::ObjectValue(*obj);
943 }
944 
GetUniformLocation(const nsAString & userName_wide) const945 already_AddRefed<WebGLUniformLocation> WebGLProgram::GetUniformLocation(
946     const nsAString& userName_wide) const {
947   if (!ValidateGLSLVariableName(userName_wide, mContext, "getUniformLocation"))
948     return nullptr;
949 
950   if (!IsLinked()) {
951     mContext->ErrorInvalidOperation(
952         "getUniformLocation: `program` must be linked.");
953     return nullptr;
954   }
955 
956   const NS_LossyConvertUTF16toASCII userName(userName_wide);
957 
958   // GLES 2.0.25, Section 2.10, p35
959   // If the the uniform location is an array, then the location of the first
960   // element of that array can be retrieved by either using the name of the
961   // uniform array, or the name of the uniform array appended with "[0]".
962   nsCString mappedName;
963   size_t arrayIndex;
964   webgl::UniformInfo* info;
965   if (!LinkInfo()->FindUniform(userName, &mappedName, &arrayIndex, &info))
966     return nullptr;
967 
968   gl::GLContext* gl = mContext->GL();
969 
970   GLint loc = gl->fGetUniformLocation(mGLName, mappedName.BeginReading());
971   if (loc == -1) return nullptr;
972 
973   RefPtr<WebGLUniformLocation> locObj =
974       new WebGLUniformLocation(mContext, LinkInfo(), info, loc, arrayIndex);
975   return locObj.forget();
976 }
977 
GetUniformIndices(const dom::Sequence<nsString> & uniformNames,dom::Nullable<nsTArray<GLuint>> & retval) const978 void WebGLProgram::GetUniformIndices(
979     const dom::Sequence<nsString>& uniformNames,
980     dom::Nullable<nsTArray<GLuint>>& retval) const {
981   const char funcName[] = "getUniformIndices";
982   if (!IsLinked()) {
983     mContext->ErrorInvalidOperation("%s: `program` must be linked.", funcName);
984     return;
985   }
986 
987   size_t count = uniformNames.Length();
988   nsTArray<GLuint>& arr = retval.SetValue();
989 
990   gl::GLContext* gl = mContext->GL();
991 
992   for (size_t i = 0; i < count; i++) {
993     const NS_LossyConvertUTF16toASCII userName(uniformNames[i]);
994 
995     nsCString mappedName;
996     size_t arrayIndex;
997     webgl::UniformInfo* info;
998     if (!LinkInfo()->FindUniform(userName, &mappedName, &arrayIndex, &info)) {
999       arr.AppendElement(LOCAL_GL_INVALID_INDEX);
1000       continue;
1001     }
1002 
1003     const GLchar* const mappedNameBegin = mappedName.get();
1004 
1005     GLuint index = LOCAL_GL_INVALID_INDEX;
1006     gl->fGetUniformIndices(mGLName, 1, &mappedNameBegin, &index);
1007     arr.AppendElement(index);
1008   }
1009 }
1010 
UniformBlockBinding(GLuint uniformBlockIndex,GLuint uniformBlockBinding) const1011 void WebGLProgram::UniformBlockBinding(GLuint uniformBlockIndex,
1012                                        GLuint uniformBlockBinding) const {
1013   const char funcName[] = "getActiveUniformBlockName";
1014   if (!IsLinked()) {
1015     mContext->ErrorInvalidOperation("%s: `program` must be linked.", funcName);
1016     return;
1017   }
1018 
1019   const auto& uniformBlocks = LinkInfo()->uniformBlocks;
1020   if (uniformBlockIndex >= uniformBlocks.size()) {
1021     mContext->ErrorInvalidValue("%s: Index %u invalid.", funcName,
1022                                 uniformBlockIndex);
1023     return;
1024   }
1025   const auto& uniformBlock = uniformBlocks[uniformBlockIndex];
1026 
1027   const auto& indexedBindings = mContext->mIndexedUniformBufferBindings;
1028   if (uniformBlockBinding >= indexedBindings.size()) {
1029     mContext->ErrorInvalidValue("%s: Binding %u invalid.", funcName,
1030                                 uniformBlockBinding);
1031     return;
1032   }
1033   const auto& indexedBinding = indexedBindings[uniformBlockBinding];
1034 
1035   ////
1036 
1037   gl::GLContext* gl = mContext->GL();
1038   gl->fUniformBlockBinding(mGLName, uniformBlockIndex, uniformBlockBinding);
1039 
1040   ////
1041 
1042   uniformBlock->mBinding = &indexedBinding;
1043 }
1044 
ValidateForLink()1045 bool WebGLProgram::ValidateForLink() {
1046   if (!mVertShader || !mVertShader->IsCompiled()) {
1047     mLinkLog.AssignLiteral("Must have a compiled vertex shader attached.");
1048     return false;
1049   }
1050 
1051   if (!mFragShader || !mFragShader->IsCompiled()) {
1052     mLinkLog.AssignLiteral("Must have an compiled fragment shader attached.");
1053     return false;
1054   }
1055 
1056   if (!mFragShader->CanLinkTo(mVertShader, &mLinkLog)) return false;
1057 
1058   const auto& gl = mContext->gl;
1059 
1060   if (gl->WorkAroundDriverBugs() && mContext->mIsMesa) {
1061     // Bug 777028: Mesa can't handle more than 16 samplers per program,
1062     // counting each array entry.
1063     size_t numSamplerUniforms_upperBound =
1064         mVertShader->CalcNumSamplerUniforms() +
1065         mFragShader->CalcNumSamplerUniforms();
1066     if (numSamplerUniforms_upperBound > 16) {
1067       mLinkLog.AssignLiteral(
1068           "Programs with more than 16 samplers are disallowed on"
1069           " Mesa drivers to avoid crashing.");
1070       return false;
1071     }
1072 
1073     // Bug 1203135: Mesa crashes internally if we exceed the reported maximum
1074     // attribute count.
1075     if (mVertShader->NumAttributes() > mContext->MaxVertexAttribs()) {
1076       mLinkLog.AssignLiteral(
1077           "Number of attributes exceeds Mesa's reported max"
1078           " attribute count.");
1079       return false;
1080     }
1081   }
1082 
1083   return true;
1084 }
1085 
LinkProgram()1086 void WebGLProgram::LinkProgram() {
1087   const char funcName[] = "linkProgram";
1088 
1089   if (mNumActiveTFOs) {
1090     mContext->ErrorInvalidOperation(
1091         "%s: Program is in-use by one or more active"
1092         " transform feedback objects.",
1093         funcName);
1094     return;
1095   }
1096 
1097   // as some of the validation changes program state
1098 
1099   mLinkLog.Truncate();
1100   mMostRecentLinkInfo = nullptr;
1101 
1102   if (!ValidateForLink()) {
1103     mContext->GenerateWarning("%s: %s", funcName, mLinkLog.BeginReading());
1104     return;
1105   }
1106 
1107   // Bind the attrib locations.
1108   // This can't be done trivially, because we have to deal with mapped attrib
1109   // names.
1110   for (const auto& pair : mNextLink_BoundAttribLocs) {
1111     const auto& name = pair.first;
1112     const auto& index = pair.second;
1113 
1114     mVertShader->BindAttribLocation(mGLName, name, index);
1115   }
1116 
1117   // Storage for transform feedback varyings before link.
1118   // (Work around for bug seen on nVidia drivers.)
1119   std::vector<std::string> scopedMappedTFVaryings;
1120 
1121   if (mContext->IsWebGL2()) {
1122     mVertShader->MapTransformFeedbackVaryings(
1123         mNextLink_TransformFeedbackVaryings, &scopedMappedTFVaryings);
1124 
1125     std::vector<const char*> driverVaryings;
1126     driverVaryings.reserve(scopedMappedTFVaryings.size());
1127     for (const auto& cur : scopedMappedTFVaryings) {
1128       driverVaryings.push_back(cur.c_str());
1129     }
1130 
1131     mContext->gl->fTransformFeedbackVaryings(
1132         mGLName, driverVaryings.size(), driverVaryings.data(),
1133         mNextLink_TransformFeedbackBufferMode);
1134   }
1135 
1136   LinkAndUpdate();
1137 
1138   if (mMostRecentLinkInfo) {
1139     nsCString postLinkLog;
1140     if (ValidateAfterTentativeLink(&postLinkLog)) return;
1141 
1142     mMostRecentLinkInfo = nullptr;
1143     mLinkLog = postLinkLog;
1144   }
1145 
1146   // Failed link.
1147   if (mContext->ShouldGenerateWarnings()) {
1148     // report shader/program infoLogs as warnings.
1149     // note that shader compilation errors can be deferred to linkProgram,
1150     // which is why we can't do anything in compileShader. In practice we could
1151     // report in compileShader the translation errors generated by ANGLE,
1152     // but it seems saner to keep a single way of obtaining shader infologs.
1153     if (!mLinkLog.IsEmpty()) {
1154       mContext->GenerateWarning(
1155           "linkProgram: Failed to link, leaving the following"
1156           " log:\n%s\n",
1157           mLinkLog.BeginReading());
1158     }
1159   }
1160 }
1161 
NumUsedLocationsByElemType(GLenum elemType)1162 static uint8_t NumUsedLocationsByElemType(GLenum elemType) {
1163   // GLES 3.0.4 p55
1164 
1165   switch (elemType) {
1166     case LOCAL_GL_FLOAT_MAT2:
1167     case LOCAL_GL_FLOAT_MAT2x3:
1168     case LOCAL_GL_FLOAT_MAT2x4:
1169       return 2;
1170 
1171     case LOCAL_GL_FLOAT_MAT3x2:
1172     case LOCAL_GL_FLOAT_MAT3:
1173     case LOCAL_GL_FLOAT_MAT3x4:
1174       return 3;
1175 
1176     case LOCAL_GL_FLOAT_MAT4x2:
1177     case LOCAL_GL_FLOAT_MAT4x3:
1178     case LOCAL_GL_FLOAT_MAT4:
1179       return 4;
1180 
1181     default:
1182       return 1;
1183   }
1184 }
1185 
NumComponents(GLenum elemType)1186 static uint8_t NumComponents(GLenum elemType) {
1187   switch (elemType) {
1188     case LOCAL_GL_FLOAT:
1189     case LOCAL_GL_INT:
1190     case LOCAL_GL_UNSIGNED_INT:
1191     case LOCAL_GL_BOOL:
1192       return 1;
1193 
1194     case LOCAL_GL_FLOAT_VEC2:
1195     case LOCAL_GL_INT_VEC2:
1196     case LOCAL_GL_UNSIGNED_INT_VEC2:
1197     case LOCAL_GL_BOOL_VEC2:
1198       return 2;
1199 
1200     case LOCAL_GL_FLOAT_VEC3:
1201     case LOCAL_GL_INT_VEC3:
1202     case LOCAL_GL_UNSIGNED_INT_VEC3:
1203     case LOCAL_GL_BOOL_VEC3:
1204       return 3;
1205 
1206     case LOCAL_GL_FLOAT_VEC4:
1207     case LOCAL_GL_INT_VEC4:
1208     case LOCAL_GL_UNSIGNED_INT_VEC4:
1209     case LOCAL_GL_BOOL_VEC4:
1210     case LOCAL_GL_FLOAT_MAT2:
1211       return 4;
1212 
1213     case LOCAL_GL_FLOAT_MAT2x3:
1214     case LOCAL_GL_FLOAT_MAT3x2:
1215       return 6;
1216 
1217     case LOCAL_GL_FLOAT_MAT2x4:
1218     case LOCAL_GL_FLOAT_MAT4x2:
1219       return 8;
1220 
1221     case LOCAL_GL_FLOAT_MAT3:
1222       return 9;
1223 
1224     case LOCAL_GL_FLOAT_MAT3x4:
1225     case LOCAL_GL_FLOAT_MAT4x3:
1226       return 12;
1227 
1228     case LOCAL_GL_FLOAT_MAT4:
1229       return 16;
1230 
1231     default:
1232       MOZ_CRASH("`elemType`");
1233   }
1234 }
1235 
ValidateAfterTentativeLink(nsCString * const out_linkLog) const1236 bool WebGLProgram::ValidateAfterTentativeLink(
1237     nsCString* const out_linkLog) const {
1238   const auto& linkInfo = mMostRecentLinkInfo;
1239   const auto& gl = mContext->gl;
1240 
1241   // Check if the attrib name conflicting to uniform name
1242   for (const auto& attrib : linkInfo->attribs) {
1243     const auto& attribName = attrib.mActiveInfo->mBaseUserName;
1244 
1245     for (const auto& uniform : linkInfo->uniforms) {
1246       const auto& uniformName = uniform->mActiveInfo->mBaseUserName;
1247       if (attribName == uniformName) {
1248         *out_linkLog = nsPrintfCString(
1249             "Attrib name conflicts with uniform name:"
1250             " %s",
1251             attribName.BeginReading());
1252         return false;
1253       }
1254     }
1255   }
1256 
1257   std::map<uint32_t, const webgl::AttribInfo*> attribsByLoc;
1258   for (const auto& attrib : linkInfo->attribs) {
1259     if (attrib.mLoc == -1) continue;
1260 
1261     const auto& elemType = attrib.mActiveInfo->mElemType;
1262     const auto numUsedLocs = NumUsedLocationsByElemType(elemType);
1263     for (uint32_t i = 0; i < numUsedLocs; i++) {
1264       const uint32_t usedLoc = attrib.mLoc + i;
1265 
1266       const auto res = attribsByLoc.insert({usedLoc, &attrib});
1267       const bool& didInsert = res.second;
1268       if (!didInsert) {
1269         const auto& aliasingName = attrib.mActiveInfo->mBaseUserName;
1270         const auto& itrExisting = res.first;
1271         const auto& existingInfo = itrExisting->second;
1272         const auto& existingName = existingInfo->mActiveInfo->mBaseUserName;
1273         *out_linkLog = nsPrintfCString(
1274             "Attrib \"%s\" aliases locations used by"
1275             " attrib \"%s\".",
1276             aliasingName.BeginReading(), existingName.BeginReading());
1277         return false;
1278       }
1279     }
1280   }
1281 
1282   // Forbid:
1283   // * Unrecognized varying name
1284   // * Duplicate varying name
1285   // * Too many components for specified buffer mode
1286   if (!mNextLink_TransformFeedbackVaryings.empty()) {
1287     GLuint maxComponentsPerIndex = 0;
1288     switch (mNextLink_TransformFeedbackBufferMode) {
1289       case LOCAL_GL_INTERLEAVED_ATTRIBS:
1290         gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS,
1291                          &maxComponentsPerIndex);
1292         break;
1293 
1294       case LOCAL_GL_SEPARATE_ATTRIBS:
1295         gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS,
1296                          &maxComponentsPerIndex);
1297         break;
1298 
1299       default:
1300         MOZ_CRASH("`bufferMode`");
1301     }
1302 
1303     std::vector<size_t> componentsPerVert;
1304     std::set<const WebGLActiveInfo*> alreadyUsed;
1305     for (const auto& wideUserName : mNextLink_TransformFeedbackVaryings) {
1306       if (componentsPerVert.empty() ||
1307           mNextLink_TransformFeedbackBufferMode == LOCAL_GL_SEPARATE_ATTRIBS) {
1308         componentsPerVert.push_back(0);
1309       }
1310 
1311       ////
1312 
1313       const WebGLActiveInfo* curInfo = nullptr;
1314       for (const auto& info : linkInfo->transformFeedbackVaryings) {
1315         const NS_ConvertASCIItoUTF16 info_wideUserName(info->mBaseUserName);
1316         if (info_wideUserName == wideUserName) {
1317           curInfo = info.get();
1318           break;
1319         }
1320       }
1321 
1322       if (!curInfo) {
1323         const NS_LossyConvertUTF16toASCII asciiUserName(wideUserName);
1324         *out_linkLog = nsPrintfCString(
1325             "Transform feedback varying \"%s\" not"
1326             " found.",
1327             asciiUserName.BeginReading());
1328         return false;
1329       }
1330 
1331       const auto insertResPair = alreadyUsed.insert(curInfo);
1332       const auto& didInsert = insertResPair.second;
1333       if (!didInsert) {
1334         const NS_LossyConvertUTF16toASCII asciiUserName(wideUserName);
1335         *out_linkLog = nsPrintfCString(
1336             "Transform feedback varying \"%s\""
1337             " specified twice.",
1338             asciiUserName.BeginReading());
1339         return false;
1340       }
1341 
1342       ////
1343 
1344       size_t varyingComponents = NumComponents(curInfo->mElemType);
1345       varyingComponents *= curInfo->mElemCount;
1346 
1347       auto& totalComponentsForIndex = *(componentsPerVert.rbegin());
1348       totalComponentsForIndex += varyingComponents;
1349 
1350       if (totalComponentsForIndex > maxComponentsPerIndex) {
1351         const NS_LossyConvertUTF16toASCII asciiUserName(wideUserName);
1352         *out_linkLog = nsPrintfCString(
1353             "Transform feedback varying \"%s\""
1354             " pushed `componentsForIndex` over the"
1355             " limit of %u.",
1356             asciiUserName.BeginReading(), maxComponentsPerIndex);
1357         return false;
1358       }
1359     }
1360 
1361     linkInfo->componentsPerTFVert.swap(componentsPerVert);
1362   }
1363 
1364   return true;
1365 }
1366 
UseProgram() const1367 bool WebGLProgram::UseProgram() const {
1368   const char funcName[] = "useProgram";
1369 
1370   if (!mMostRecentLinkInfo) {
1371     mContext->ErrorInvalidOperation(
1372         "%s: Program has not been successfully linked.", funcName);
1373     return false;
1374   }
1375 
1376   if (mContext->mBoundTransformFeedback &&
1377       mContext->mBoundTransformFeedback->mIsActive &&
1378       !mContext->mBoundTransformFeedback->mIsPaused) {
1379     mContext->ErrorInvalidOperation(
1380         "%s: Transform feedback active and not paused.", funcName);
1381     return false;
1382   }
1383 
1384   mContext->gl->fUseProgram(mGLName);
1385   return true;
1386 }
1387 
ValidateProgram() const1388 void WebGLProgram::ValidateProgram() const {
1389   gl::GLContext* gl = mContext->gl;
1390 
1391 #ifdef XP_MACOSX
1392   // See bug 593867 for NVIDIA and bug 657201 for ATI. The latter is confirmed
1393   // with Mac OS 10.6.7.
1394   if (gl->WorkAroundDriverBugs()) {
1395     mContext->GenerateWarning(
1396         "validateProgram: Implemented as a no-op on"
1397         " Mac to work around crashes.");
1398     return;
1399   }
1400 #endif
1401 
1402   gl->fValidateProgram(mGLName);
1403 }
1404 
1405 ////////////////////////////////////////////////////////////////////////////////
1406 
LinkAndUpdate()1407 void WebGLProgram::LinkAndUpdate() {
1408   mMostRecentLinkInfo = nullptr;
1409 
1410   gl::GLContext* gl = mContext->gl;
1411   gl->fLinkProgram(mGLName);
1412 
1413   // Grab the program log.
1414   GLuint logLenWithNull = 0;
1415   gl->fGetProgramiv(mGLName, LOCAL_GL_INFO_LOG_LENGTH, (GLint*)&logLenWithNull);
1416   if (logLenWithNull > 1) {
1417     mLinkLog.SetLength(logLenWithNull - 1);
1418     gl->fGetProgramInfoLog(mGLName, logLenWithNull, nullptr,
1419                            mLinkLog.BeginWriting());
1420   } else {
1421     mLinkLog.SetLength(0);
1422   }
1423 
1424   GLint ok = 0;
1425   gl->fGetProgramiv(mGLName, LOCAL_GL_LINK_STATUS, &ok);
1426   if (!ok) return;
1427 
1428   mMostRecentLinkInfo = QueryProgramInfo(this, gl);
1429   MOZ_RELEASE_ASSERT(mMostRecentLinkInfo,
1430                      "GFX: most recent link info not set.");
1431 }
1432 
FindAttribUserNameByMappedName(const nsACString & mappedName,nsCString * const out_userName) const1433 bool WebGLProgram::FindAttribUserNameByMappedName(
1434     const nsACString& mappedName, nsCString* const out_userName) const {
1435   if (mVertShader->FindAttribUserNameByMappedName(mappedName, out_userName))
1436     return true;
1437 
1438   return false;
1439 }
1440 
FindVaryingByMappedName(const nsACString & mappedName,nsCString * const out_userName,bool * const out_isArray) const1441 bool WebGLProgram::FindVaryingByMappedName(const nsACString& mappedName,
1442                                            nsCString* const out_userName,
1443                                            bool* const out_isArray) const {
1444   if (mVertShader->FindVaryingByMappedName(mappedName, out_userName,
1445                                            out_isArray))
1446     return true;
1447 
1448   return false;
1449 }
1450 
FindUniformByMappedName(const nsACString & mappedName,nsCString * const out_userName,bool * const out_isArray) const1451 bool WebGLProgram::FindUniformByMappedName(const nsACString& mappedName,
1452                                            nsCString* const out_userName,
1453                                            bool* const out_isArray) const {
1454   if (mVertShader->FindUniformByMappedName(mappedName, out_userName,
1455                                            out_isArray))
1456     return true;
1457 
1458   if (mFragShader->FindUniformByMappedName(mappedName, out_userName,
1459                                            out_isArray))
1460     return true;
1461 
1462   return false;
1463 }
1464 
TransformFeedbackVaryings(const dom::Sequence<nsString> & varyings,GLenum bufferMode)1465 void WebGLProgram::TransformFeedbackVaryings(
1466     const dom::Sequence<nsString>& varyings, GLenum bufferMode) {
1467   const char funcName[] = "transformFeedbackVaryings";
1468 
1469   const auto& gl = mContext->gl;
1470 
1471   switch (bufferMode) {
1472     case LOCAL_GL_INTERLEAVED_ATTRIBS:
1473       break;
1474 
1475     case LOCAL_GL_SEPARATE_ATTRIBS: {
1476       GLuint maxAttribs = 0;
1477       gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS,
1478                        &maxAttribs);
1479       if (varyings.Length() > maxAttribs) {
1480         mContext->ErrorInvalidValue("%s: Length of `varyings` exceeds %s.",
1481                                     funcName,
1482                                     "TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS");
1483         return;
1484       }
1485     } break;
1486 
1487     default:
1488       mContext->ErrorInvalidEnum("%s: Bad `bufferMode`: 0x%04x.", funcName,
1489                                  bufferMode);
1490       return;
1491   }
1492 
1493   ////
1494 
1495   mNextLink_TransformFeedbackVaryings.assign(
1496       varyings.Elements(), varyings.Elements() + varyings.Length());
1497   mNextLink_TransformFeedbackBufferMode = bufferMode;
1498 }
1499 
GetTransformFeedbackVarying(GLuint index) const1500 already_AddRefed<WebGLActiveInfo> WebGLProgram::GetTransformFeedbackVarying(
1501     GLuint index) const {
1502   // No docs in the WebGL 2 spec for this function. Taking the language for
1503   // getActiveAttrib, which states that the function returns null on any error.
1504   if (!IsLinked()) {
1505     mContext->ErrorInvalidOperation(
1506         "getTransformFeedbackVarying: `program` must be "
1507         "linked.");
1508     return nullptr;
1509   }
1510 
1511   if (index >= LinkInfo()->transformFeedbackVaryings.size()) {
1512     mContext->ErrorInvalidValue(
1513         "getTransformFeedbackVarying: `index` is greater or "
1514         "equal to TRANSFORM_FEEDBACK_VARYINGS.");
1515     return nullptr;
1516   }
1517 
1518   RefPtr<WebGLActiveInfo> ret = LinkInfo()->transformFeedbackVaryings[index];
1519   return ret.forget();
1520 }
1521 
UnmapUniformBlockName(const nsCString & mappedName,nsCString * const out_userName) const1522 bool WebGLProgram::UnmapUniformBlockName(const nsCString& mappedName,
1523                                          nsCString* const out_userName) const {
1524   nsCString baseMappedName;
1525   bool isArray;
1526   size_t arrayIndex;
1527   if (!ParseName(mappedName, &baseMappedName, &isArray, &arrayIndex))
1528     return false;
1529 
1530   nsCString baseUserName;
1531   if (!mVertShader->UnmapUniformBlockName(baseMappedName, &baseUserName) &&
1532       !mFragShader->UnmapUniformBlockName(baseMappedName, &baseUserName)) {
1533     return false;
1534   }
1535 
1536   AssembleName(baseUserName, isArray, arrayIndex, out_userName);
1537   return true;
1538 }
1539 
EnumerateFragOutputs(std::map<nsCString,const nsCString> & out_FragOutputs) const1540 void WebGLProgram::EnumerateFragOutputs(
1541     std::map<nsCString, const nsCString>& out_FragOutputs) const {
1542   MOZ_ASSERT(mFragShader);
1543 
1544   mFragShader->EnumerateFragOutputs(out_FragOutputs);
1545 }
1546 
1547 ////////////////////////////////////////////////////////////////////////////////
1548 
IsBaseName(const nsCString & name)1549 bool IsBaseName(const nsCString& name) {
1550   if (!name.Length()) return true;
1551 
1552   return name[name.Length() - 1] != ']';  // Doesn't end in ']'.
1553 }
1554 
FindAttrib(const nsCString & userName,const webgl::AttribInfo ** const out) const1555 bool webgl::LinkedProgramInfo::FindAttrib(
1556     const nsCString& userName, const webgl::AttribInfo** const out) const {
1557   // VS inputs cannot be arrays or structures.
1558   // `userName` is thus always `baseUserName`.
1559   for (const auto& attrib : attribs) {
1560     if (attrib.mActiveInfo->mBaseUserName == userName) {
1561       *out = &attrib;
1562       return true;
1563     }
1564   }
1565 
1566   return false;
1567 }
1568 
FindUniform(const nsCString & userName,nsCString * const out_mappedName,size_t * const out_arrayIndex,webgl::UniformInfo ** const out_info) const1569 bool webgl::LinkedProgramInfo::FindUniform(
1570     const nsCString& userName, nsCString* const out_mappedName,
1571     size_t* const out_arrayIndex, webgl::UniformInfo** const out_info) const {
1572   nsCString baseUserName;
1573   bool isArray;
1574   size_t arrayIndex;
1575   if (!ParseName(userName, &baseUserName, &isArray, &arrayIndex)) return false;
1576 
1577   webgl::UniformInfo* info = nullptr;
1578   for (const auto& uniform : uniforms) {
1579     if (uniform->mActiveInfo->mBaseUserName == baseUserName) {
1580       info = uniform;
1581       break;
1582     }
1583   }
1584   if (!info) return false;
1585 
1586   const auto& baseMappedName = info->mActiveInfo->mBaseMappedName;
1587   AssembleName(baseMappedName, isArray, arrayIndex, out_mappedName);
1588 
1589   *out_arrayIndex = arrayIndex;
1590   *out_info = info;
1591   return true;
1592 }
1593 
MapFragDataName(const nsCString & userName,nsCString * const out_mappedName) const1594 bool webgl::LinkedProgramInfo::MapFragDataName(
1595     const nsCString& userName, nsCString* const out_mappedName) const {
1596   // FS outputs can be arrays, but not structures.
1597 
1598   if (fragDataMap.empty()) {
1599     // No mappings map from validation, so just forward it.
1600     *out_mappedName = userName;
1601     return true;
1602   }
1603 
1604   nsCString baseUserName;
1605   bool isArray;
1606   size_t arrayIndex;
1607   if (!ParseName(userName, &baseUserName, &isArray, &arrayIndex)) return false;
1608 
1609   const auto itr = fragDataMap.find(baseUserName);
1610   if (itr == fragDataMap.end()) return false;
1611 
1612   const auto& baseMappedName = itr->second;
1613   AssembleName(baseMappedName, isArray, arrayIndex, out_mappedName);
1614   return true;
1615 }
1616 
1617 ////////////////////////////////////////////////////////////////////////////////
1618 
WrapObject(JSContext * js,JS::Handle<JSObject * > givenProto)1619 JSObject* WebGLProgram::WrapObject(JSContext* js,
1620                                    JS::Handle<JSObject*> givenProto) {
1621   return dom::WebGLProgramBinding::Wrap(js, this, givenProto);
1622 }
1623 
1624 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLProgram, mVertShader, mFragShader)
1625 
1626 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLProgram, AddRef)
1627 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLProgram, Release)
1628 
1629 }  // namespace mozilla
1630