1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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 "WebGLBuffer.h"
15 #include "WebGLContext.h"
16 #include "WebGLShader.h"
17 #include "WebGLShaderValidator.h"
18 #include "WebGLTransformFeedback.h"
19 #include "WebGLValidateStrings.h"
20 #include "WebGLVertexArray.h"
21 
22 namespace mozilla {
23 
IsShadowSampler(const GLenum elemType)24 static bool IsShadowSampler(const GLenum elemType) {
25   switch (elemType) {
26     case LOCAL_GL_SAMPLER_2D_SHADOW:
27     case LOCAL_GL_SAMPLER_CUBE_SHADOW:
28     case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
29       return true;
30     default:
31       return false;
32   }
33 }
34 
SamplerBaseType(const GLenum elemType)35 static Maybe<webgl::TextureBaseType> SamplerBaseType(const GLenum elemType) {
36   switch (elemType) {
37     case LOCAL_GL_SAMPLER_2D:
38     case LOCAL_GL_SAMPLER_3D:
39     case LOCAL_GL_SAMPLER_CUBE:
40     case LOCAL_GL_SAMPLER_2D_ARRAY:
41     case LOCAL_GL_SAMPLER_2D_SHADOW:
42     case LOCAL_GL_SAMPLER_CUBE_SHADOW:
43     case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
44       return Some(webgl::TextureBaseType::Float);
45 
46     case LOCAL_GL_INT_SAMPLER_2D:
47     case LOCAL_GL_INT_SAMPLER_3D:
48     case LOCAL_GL_INT_SAMPLER_CUBE:
49     case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
50       return Some(webgl::TextureBaseType::Int);
51 
52     case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
53     case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
54     case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
55     case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
56       return Some(webgl::TextureBaseType::UInt);
57 
58     default:
59       return {};
60   }
61 }
62 
63 //////////
64 
FragOutputBaseType(const GLenum type)65 static webgl::TextureBaseType FragOutputBaseType(const GLenum type) {
66   switch (type) {
67     case LOCAL_GL_FLOAT:
68     case LOCAL_GL_FLOAT_VEC2:
69     case LOCAL_GL_FLOAT_VEC3:
70     case LOCAL_GL_FLOAT_VEC4:
71       return webgl::TextureBaseType::Float;
72 
73     case LOCAL_GL_INT:
74     case LOCAL_GL_INT_VEC2:
75     case LOCAL_GL_INT_VEC3:
76     case LOCAL_GL_INT_VEC4:
77       return webgl::TextureBaseType::Int;
78 
79     case LOCAL_GL_UNSIGNED_INT:
80     case LOCAL_GL_UNSIGNED_INT_VEC2:
81     case LOCAL_GL_UNSIGNED_INT_VEC3:
82     case LOCAL_GL_UNSIGNED_INT_VEC4:
83       return webgl::TextureBaseType::UInt;
84 
85     default:
86       break;
87   }
88 
89   const auto& str = EnumString(type);
90   gfxCriticalError() << "Unhandled enum for FragOutputBaseType: "
91                      << str.c_str();
92   return webgl::TextureBaseType::Float;
93 }
94 
95 // -----------------------------------------
96 
97 namespace webgl {
98 
UniformAs1fv(gl::GLContext & gl,GLint location,GLsizei count,bool transpose,const void * any)99 void UniformAs1fv(gl::GLContext& gl, GLint location, GLsizei count,
100                   bool transpose, const void* any) {
101   gl.fUniform1fv(location, count, static_cast<const float*>(any));
102 }
UniformAs2fv(gl::GLContext & gl,GLint location,GLsizei count,bool transpose,const void * any)103 void UniformAs2fv(gl::GLContext& gl, GLint location, GLsizei count,
104                   bool transpose, const void* any) {
105   gl.fUniform2fv(location, count, static_cast<const float*>(any));
106 }
UniformAs3fv(gl::GLContext & gl,GLint location,GLsizei count,bool transpose,const void * any)107 void UniformAs3fv(gl::GLContext& gl, GLint location, GLsizei count,
108                   bool transpose, const void* any) {
109   gl.fUniform3fv(location, count, static_cast<const float*>(any));
110 }
UniformAs4fv(gl::GLContext & gl,GLint location,GLsizei count,bool transpose,const void * any)111 void UniformAs4fv(gl::GLContext& gl, GLint location, GLsizei count,
112                   bool transpose, const void* any) {
113   gl.fUniform4fv(location, count, static_cast<const float*>(any));
114 }
115 
UniformAs1iv(gl::GLContext & gl,GLint location,GLsizei count,bool transpose,const void * any)116 void UniformAs1iv(gl::GLContext& gl, GLint location, GLsizei count,
117                   bool transpose, const void* any) {
118   gl.fUniform1iv(location, count, static_cast<const int32_t*>(any));
119 }
UniformAs2iv(gl::GLContext & gl,GLint location,GLsizei count,bool transpose,const void * any)120 void UniformAs2iv(gl::GLContext& gl, GLint location, GLsizei count,
121                   bool transpose, const void* any) {
122   gl.fUniform2iv(location, count, static_cast<const int32_t*>(any));
123 }
UniformAs3iv(gl::GLContext & gl,GLint location,GLsizei count,bool transpose,const void * any)124 void UniformAs3iv(gl::GLContext& gl, GLint location, GLsizei count,
125                   bool transpose, const void* any) {
126   gl.fUniform3iv(location, count, static_cast<const int32_t*>(any));
127 }
UniformAs4iv(gl::GLContext & gl,GLint location,GLsizei count,bool transpose,const void * any)128 void UniformAs4iv(gl::GLContext& gl, GLint location, GLsizei count,
129                   bool transpose, const void* any) {
130   gl.fUniform4iv(location, count, static_cast<const int32_t*>(any));
131 }
132 
UniformAs1uiv(gl::GLContext & gl,GLint location,GLsizei count,bool transpose,const void * any)133 void UniformAs1uiv(gl::GLContext& gl, GLint location, GLsizei count,
134                    bool transpose, const void* any) {
135   gl.fUniform1uiv(location, count, static_cast<const uint32_t*>(any));
136 }
UniformAs2uiv(gl::GLContext & gl,GLint location,GLsizei count,bool transpose,const void * any)137 void UniformAs2uiv(gl::GLContext& gl, GLint location, GLsizei count,
138                    bool transpose, const void* any) {
139   gl.fUniform2uiv(location, count, static_cast<const uint32_t*>(any));
140 }
UniformAs3uiv(gl::GLContext & gl,GLint location,GLsizei count,bool transpose,const void * any)141 void UniformAs3uiv(gl::GLContext& gl, GLint location, GLsizei count,
142                    bool transpose, const void* any) {
143   gl.fUniform3uiv(location, count, static_cast<const uint32_t*>(any));
144 }
UniformAs4uiv(gl::GLContext & gl,GLint location,GLsizei count,bool transpose,const void * any)145 void UniformAs4uiv(gl::GLContext& gl, GLint location, GLsizei count,
146                    bool transpose, const void* any) {
147   gl.fUniform4uiv(location, count, static_cast<const uint32_t*>(any));
148 }
149 
UniformAsMatrix2x2fv(gl::GLContext & gl,GLint location,GLsizei count,bool transpose,const void * any)150 void UniformAsMatrix2x2fv(gl::GLContext& gl, GLint location, GLsizei count,
151                           bool transpose, const void* any) {
152   gl.fUniformMatrix2fv(location, count, transpose,
153                        static_cast<const float*>(any));
154 }
UniformAsMatrix2x3fv(gl::GLContext & gl,GLint location,GLsizei count,bool transpose,const void * any)155 void UniformAsMatrix2x3fv(gl::GLContext& gl, GLint location, GLsizei count,
156                           bool transpose, const void* any) {
157   gl.fUniformMatrix2x3fv(location, count, transpose,
158                          static_cast<const float*>(any));
159 }
UniformAsMatrix2x4fv(gl::GLContext & gl,GLint location,GLsizei count,bool transpose,const void * any)160 void UniformAsMatrix2x4fv(gl::GLContext& gl, GLint location, GLsizei count,
161                           bool transpose, const void* any) {
162   gl.fUniformMatrix2x4fv(location, count, transpose,
163                          static_cast<const float*>(any));
164 }
165 
UniformAsMatrix3x2fv(gl::GLContext & gl,GLint location,GLsizei count,bool transpose,const void * any)166 void UniformAsMatrix3x2fv(gl::GLContext& gl, GLint location, GLsizei count,
167                           bool transpose, const void* any) {
168   gl.fUniformMatrix3x2fv(location, count, transpose,
169                          static_cast<const float*>(any));
170 }
UniformAsMatrix3x3fv(gl::GLContext & gl,GLint location,GLsizei count,bool transpose,const void * any)171 void UniformAsMatrix3x3fv(gl::GLContext& gl, GLint location, GLsizei count,
172                           bool transpose, const void* any) {
173   gl.fUniformMatrix3fv(location, count, transpose,
174                        static_cast<const float*>(any));
175 }
UniformAsMatrix3x4fv(gl::GLContext & gl,GLint location,GLsizei count,bool transpose,const void * any)176 void UniformAsMatrix3x4fv(gl::GLContext& gl, GLint location, GLsizei count,
177                           bool transpose, const void* any) {
178   gl.fUniformMatrix3x4fv(location, count, transpose,
179                          static_cast<const float*>(any));
180 }
181 
UniformAsMatrix4x2fv(gl::GLContext & gl,GLint location,GLsizei count,bool transpose,const void * any)182 void UniformAsMatrix4x2fv(gl::GLContext& gl, GLint location, GLsizei count,
183                           bool transpose, const void* any) {
184   gl.fUniformMatrix4x2fv(location, count, transpose,
185                          static_cast<const float*>(any));
186 }
UniformAsMatrix4x3fv(gl::GLContext & gl,GLint location,GLsizei count,bool transpose,const void * any)187 void UniformAsMatrix4x3fv(gl::GLContext& gl, GLint location, GLsizei count,
188                           bool transpose, const void* any) {
189   gl.fUniformMatrix4x3fv(location, count, transpose,
190                          static_cast<const float*>(any));
191 }
UniformAsMatrix4x4fv(gl::GLContext & gl,GLint location,GLsizei count,bool transpose,const void * any)192 void UniformAsMatrix4x4fv(gl::GLContext& gl, GLint location, GLsizei count,
193                           bool transpose, const void* any) {
194   gl.fUniformMatrix4fv(location, count, transpose,
195                        static_cast<const float*>(any));
196 }
197 
198 // -
199 
EndsWith(const std::string & str,const std::string & needle)200 static bool EndsWith(const std::string& str, const std::string& needle) {
201   if (str.length() < needle.length()) return false;
202   return str.compare(str.length() - needle.length(), needle.length(), needle) ==
203          0;
204 }
205 
Make(const webgl::ActiveUniformInfo & info)206 webgl::ActiveUniformValidationInfo webgl::ActiveUniformValidationInfo::Make(
207     const webgl::ActiveUniformInfo& info) {
208   auto ret = webgl::ActiveUniformValidationInfo{info};
209   ret.isArray = EndsWith(info.name, "[0]");
210 
211   switch (info.elemType) {
212     case LOCAL_GL_FLOAT:
213       ret.channelsPerElem = 1;
214       ret.pfn = &UniformAs1fv;
215       break;
216     case LOCAL_GL_FLOAT_VEC2:
217       ret.channelsPerElem = 2;
218       ret.pfn = &UniformAs2fv;
219       break;
220     case LOCAL_GL_FLOAT_VEC3:
221       ret.channelsPerElem = 3;
222       ret.pfn = &UniformAs3fv;
223       break;
224     case LOCAL_GL_FLOAT_VEC4:
225       ret.channelsPerElem = 4;
226       ret.pfn = &UniformAs4fv;
227       break;
228 
229     case LOCAL_GL_SAMPLER_2D:
230     case LOCAL_GL_SAMPLER_3D:
231     case LOCAL_GL_SAMPLER_CUBE:
232     case LOCAL_GL_SAMPLER_2D_SHADOW:
233     case LOCAL_GL_SAMPLER_2D_ARRAY:
234     case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
235     case LOCAL_GL_SAMPLER_CUBE_SHADOW:
236     case LOCAL_GL_INT_SAMPLER_2D:
237     case LOCAL_GL_INT_SAMPLER_3D:
238     case LOCAL_GL_INT_SAMPLER_CUBE:
239     case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
240     case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
241     case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
242     case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
243     case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
244     case LOCAL_GL_BOOL:
245     case LOCAL_GL_INT:
246       ret.channelsPerElem = 1;
247       ret.pfn = &UniformAs1iv;
248       break;
249     case LOCAL_GL_BOOL_VEC2:
250     case LOCAL_GL_INT_VEC2:
251       ret.channelsPerElem = 2;
252       ret.pfn = &UniformAs2iv;
253       break;
254     case LOCAL_GL_BOOL_VEC3:
255     case LOCAL_GL_INT_VEC3:
256       ret.channelsPerElem = 3;
257       ret.pfn = &UniformAs3iv;
258       break;
259     case LOCAL_GL_BOOL_VEC4:
260     case LOCAL_GL_INT_VEC4:
261       ret.channelsPerElem = 4;
262       ret.pfn = &UniformAs4iv;
263       break;
264 
265     case LOCAL_GL_UNSIGNED_INT:
266       ret.channelsPerElem = 1;
267       ret.pfn = &UniformAs1uiv;
268       break;
269     case LOCAL_GL_UNSIGNED_INT_VEC2:
270       ret.channelsPerElem = 2;
271       ret.pfn = &UniformAs2uiv;
272       break;
273     case LOCAL_GL_UNSIGNED_INT_VEC3:
274       ret.channelsPerElem = 3;
275       ret.pfn = &UniformAs3uiv;
276       break;
277     case LOCAL_GL_UNSIGNED_INT_VEC4:
278       ret.channelsPerElem = 4;
279       ret.pfn = &UniformAs4uiv;
280       break;
281 
282       // -
283 
284     case LOCAL_GL_FLOAT_MAT2:
285       ret.channelsPerElem = 2 * 2;
286       ret.pfn = &UniformAsMatrix2x2fv;
287       break;
288     case LOCAL_GL_FLOAT_MAT2x3:
289       ret.channelsPerElem = 2 * 3;
290       ret.pfn = &UniformAsMatrix2x3fv;
291       break;
292     case LOCAL_GL_FLOAT_MAT2x4:
293       ret.channelsPerElem = 2 * 4;
294       ret.pfn = &UniformAsMatrix2x4fv;
295       break;
296 
297     case LOCAL_GL_FLOAT_MAT3x2:
298       ret.channelsPerElem = 3 * 2;
299       ret.pfn = &UniformAsMatrix3x2fv;
300       break;
301     case LOCAL_GL_FLOAT_MAT3:
302       ret.channelsPerElem = 3 * 3;
303       ret.pfn = &UniformAsMatrix3x3fv;
304       break;
305     case LOCAL_GL_FLOAT_MAT3x4:
306       ret.channelsPerElem = 3 * 4;
307       ret.pfn = &UniformAsMatrix3x4fv;
308       break;
309 
310     case LOCAL_GL_FLOAT_MAT4x2:
311       ret.channelsPerElem = 4 * 2;
312       ret.pfn = &UniformAsMatrix4x2fv;
313       break;
314     case LOCAL_GL_FLOAT_MAT4x3:
315       ret.channelsPerElem = 4 * 3;
316       ret.pfn = &UniformAsMatrix4x3fv;
317       break;
318     case LOCAL_GL_FLOAT_MAT4:
319       ret.channelsPerElem = 4 * 4;
320       ret.pfn = &UniformAsMatrix4x4fv;
321       break;
322 
323     default:
324       gfxCriticalError() << "Bad `elemType`: " << EnumString(info.elemType);
325       MOZ_CRASH("`elemType`");
326   }
327   return ret;
328 }
329 
330 }  // namespace webgl
331 
332 // -------------------------
333 
334 //#define DUMP_SHADERVAR_MAPPINGS
335 
QueryProgramInfo(WebGLProgram * prog,gl::GLContext * gl)336 RefPtr<const webgl::LinkedProgramInfo> QueryProgramInfo(WebGLProgram* prog,
337                                                         gl::GLContext* gl) {
338   WebGLContext* const webgl = prog->mContext;
339 
340   RefPtr<webgl::LinkedProgramInfo> info(new webgl::LinkedProgramInfo(prog));
341 
342   // Frag outputs
343 
344   {
345     const auto& fragShader = prog->FragShader();
346     const auto& compileResults = fragShader->CompileResults();
347     const auto version = compileResults->mShaderVersion;
348 
349     const auto fnAddInfo = [&](const webgl::FragOutputInfo& x) {
350       info->fragOutputs.insert({x.loc, x});
351     };
352 
353     if (version == 300) {
354       for (const auto& cur : compileResults->mOutputVariables) {
355         auto loc = cur.location;
356         if (loc == -1) loc = 0;
357 
358         const auto info =
359             webgl::FragOutputInfo{uint8_t(loc), cur.name, cur.mappedName,
360                                   FragOutputBaseType(cur.type)};
361         if (!cur.isArray()) {
362           fnAddInfo(info);
363           continue;
364         }
365         MOZ_ASSERT(cur.arraySizes.size() == 1);
366         for (uint32_t i = 0; i < cur.arraySizes[0]; ++i) {
367           const auto indexStr = std::string("[") + std::to_string(i) + "]";
368 
369           const auto userName = info.userName + indexStr;
370           const auto mappedName = info.mappedName + indexStr;
371 
372           const auto indexedInfo = webgl::FragOutputInfo{
373               uint8_t(info.loc + i), userName, mappedName, info.baseType};
374           fnAddInfo(indexedInfo);
375         }
376       }
377     } else {
378       // ANGLE's translator doesn't tell us about non-user frag outputs. :(
379 
380       const auto& translatedSource = compileResults->mObjectCode;
381       uint32_t drawBuffers = 1;
382       if (translatedSource.find("(gl_FragData[1]") != std::string::npos ||
383           translatedSource.find("(webgl_FragData[1]") != std::string::npos) {
384         // The matching with the leading '(' prevents cleverly-named user vars
385         // breaking this. Since ANGLE initializes all outputs, if this is an MRT
386         // shader, FragData[1] will be present. FragData[0] is valid for non-MRT
387         // shaders.
388         drawBuffers = webgl->GLMaxDrawBuffers();
389       } else if (translatedSource.find("(gl_FragColor") == std::string::npos &&
390                  translatedSource.find("(webgl_FragColor") ==
391                      std::string::npos &&
392                  translatedSource.find("(gl_FragData") == std::string::npos &&
393                  translatedSource.find("(webgl_FragData") ==
394                      std::string::npos) {
395         // We have to support no-color-output shaders?
396         drawBuffers = 0;
397       }
398 
399       for (uint32_t i = 0; i < drawBuffers; ++i) {
400         const auto name = std::string("gl_FragData[") + std::to_string(i) + "]";
401         const auto info = webgl::FragOutputInfo{uint8_t(i), name, name,
402                                                 webgl::TextureBaseType::Float};
403         fnAddInfo(info);
404       }
405     }
406   }
407 
408   const auto& vertShader = prog->VertShader();
409   const auto& vertCompileResults = vertShader->CompileResults();
410   const auto numViews = vertCompileResults->mVertexShaderNumViews;
411   if (numViews != -1) {
412     info->zLayerCount = AssertedCast<uint8_t>(numViews);
413   }
414 
415   // -
416 
417   auto& nameMap = info->nameMap;
418 
419   const auto fnAccum = [&](WebGLShader& shader) {
420     const auto& compRes = shader.CompileResults();
421     for (const auto& pair : compRes->mNameMap) {
422       nameMap.insert(pair);
423     }
424   };
425   fnAccum(*prog->VertShader());
426   fnAccum(*prog->FragShader());
427 
428   // -
429 
430   std::unordered_map<std::string, std::string> nameUnmap;
431   for (const auto& pair : nameMap) {
432     nameUnmap.insert({pair.second, pair.first});
433   }
434 
435   info->active =
436       GetLinkActiveInfo(*gl, prog->mGLName, webgl->IsWebGL2(), nameUnmap);
437 
438   // -
439 
440   for (const auto& attrib : info->active.activeAttribs) {
441     if (attrib.location == 0) {
442       info->attrib0Active = true;
443       break;
444     }
445   }
446 
447   // -
448 
449   for (const auto& uniform : info->active.activeUniforms) {
450     const auto& elemType = uniform.elemType;
451     webgl::SamplerUniformInfo* samplerInfo = nullptr;
452     const auto baseType = SamplerBaseType(elemType);
453     if (baseType) {
454       const bool isShadowSampler = IsShadowSampler(elemType);
455 
456       auto* texList = &webgl->mBound2DTextures;
457 
458       switch (elemType) {
459         case LOCAL_GL_SAMPLER_2D:
460         case LOCAL_GL_SAMPLER_2D_SHADOW:
461         case LOCAL_GL_INT_SAMPLER_2D:
462         case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
463           break;
464 
465         case LOCAL_GL_SAMPLER_CUBE:
466         case LOCAL_GL_SAMPLER_CUBE_SHADOW:
467         case LOCAL_GL_INT_SAMPLER_CUBE:
468         case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
469           texList = &webgl->mBoundCubeMapTextures;
470           break;
471 
472         case LOCAL_GL_SAMPLER_3D:
473         case LOCAL_GL_INT_SAMPLER_3D:
474         case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
475           texList = &webgl->mBound3DTextures;
476           break;
477 
478         case LOCAL_GL_SAMPLER_2D_ARRAY:
479         case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
480         case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
481         case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
482           texList = &webgl->mBound2DArrayTextures;
483           break;
484       }
485 
486       auto curInfo = std::unique_ptr<webgl::SamplerUniformInfo>(
487           new webgl::SamplerUniformInfo{*texList, *baseType, isShadowSampler});
488       curInfo->texUnits.resize(uniform.elemCount);
489       samplerInfo = curInfo.get();
490       info->samplerUniforms.push_back(std::move(curInfo));
491     }
492 
493     const auto valInfo = webgl::ActiveUniformValidationInfo::Make(uniform);
494 
495     for (const auto& pair : uniform.locByIndex) {
496       info->locationMap.insert(
497           {pair.second, {valInfo, pair.first, samplerInfo}});
498     }
499   }
500 
501   // -
502 
503   {
504     const auto& activeBlocks = info->active.activeUniformBlocks;
505     info->uniformBlocks.reserve(activeBlocks.size());
506     for (const auto& cur : activeBlocks) {
507       const auto curInfo = webgl::UniformBlockInfo{
508           cur, &webgl->mIndexedUniformBufferBindings[0]};
509       info->uniformBlocks.push_back(curInfo);
510     }
511   }
512 
513   return info;
514 }
515 
516 ////////////////////////////////////////////////////////////////////////////////
517 
LinkedProgramInfo(WebGLProgram * prog)518 webgl::LinkedProgramInfo::LinkedProgramInfo(WebGLProgram* prog)
519     : prog(prog),
520       transformFeedbackBufferMode(prog->mNextLink_TransformFeedbackBufferMode) {
521 }
522 
523 webgl::LinkedProgramInfo::~LinkedProgramInfo() = default;
524 
ToAttribBaseType(const GLenum elemType)525 webgl::AttribBaseType webgl::ToAttribBaseType(const GLenum elemType) {
526   switch (elemType) {
527     case LOCAL_GL_BOOL:
528     case LOCAL_GL_BOOL_VEC2:
529     case LOCAL_GL_BOOL_VEC3:
530     case LOCAL_GL_BOOL_VEC4:
531       return webgl::AttribBaseType::Boolean;
532 
533     case LOCAL_GL_FLOAT:
534     case LOCAL_GL_FLOAT_VEC2:
535     case LOCAL_GL_FLOAT_VEC3:
536     case LOCAL_GL_FLOAT_VEC4:
537     case LOCAL_GL_FLOAT_MAT2:
538     case LOCAL_GL_FLOAT_MAT2x3:
539     case LOCAL_GL_FLOAT_MAT3x2:
540     case LOCAL_GL_FLOAT_MAT2x4:
541     case LOCAL_GL_FLOAT_MAT4x2:
542     case LOCAL_GL_FLOAT_MAT3:
543     case LOCAL_GL_FLOAT_MAT3x4:
544     case LOCAL_GL_FLOAT_MAT4x3:
545     case LOCAL_GL_FLOAT_MAT4:
546       return webgl::AttribBaseType::Float;
547 
548     case LOCAL_GL_INT:
549     case LOCAL_GL_INT_VEC2:
550     case LOCAL_GL_INT_VEC3:
551     case LOCAL_GL_INT_VEC4:
552     case LOCAL_GL_SAMPLER_2D:
553     case LOCAL_GL_SAMPLER_3D:
554     case LOCAL_GL_SAMPLER_CUBE:
555     case LOCAL_GL_SAMPLER_2D_SHADOW:
556     case LOCAL_GL_SAMPLER_2D_ARRAY:
557     case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
558     case LOCAL_GL_SAMPLER_CUBE_SHADOW:
559     case LOCAL_GL_INT_SAMPLER_2D:
560     case LOCAL_GL_INT_SAMPLER_3D:
561     case LOCAL_GL_INT_SAMPLER_CUBE:
562     case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
563     case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
564     case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
565     case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
566     case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
567       return webgl::AttribBaseType::Int;
568 
569     case LOCAL_GL_UNSIGNED_INT:
570     case LOCAL_GL_UNSIGNED_INT_VEC2:
571     case LOCAL_GL_UNSIGNED_INT_VEC3:
572     case LOCAL_GL_UNSIGNED_INT_VEC4:
573       return webgl::AttribBaseType::Uint;
574 
575     default:
576       gfxCriticalError() << "Bad `elemType`: " << EnumString(elemType);
577       MOZ_CRASH("`elemType`");
578   }
579 }
580 
ToString(const webgl::AttribBaseType x)581 const char* webgl::ToString(const webgl::AttribBaseType x) {
582   switch (x) {
583     case webgl::AttribBaseType::Float:
584       return "FLOAT";
585     case webgl::AttribBaseType::Int:
586       return "INT";
587     case webgl::AttribBaseType::Uint:
588       return "UINT";
589     case webgl::AttribBaseType::Boolean:
590       return "BOOL";
591   }
592   MOZ_CRASH("pacify gcc6 warning");
593 }
594 
ToString(const webgl::UniformBaseType x)595 const char* webgl::ToString(const webgl::UniformBaseType x) {
596   switch (x) {
597     case webgl::UniformBaseType::Float:
598       return "FLOAT";
599     case webgl::UniformBaseType::Int:
600       return "INT";
601     case webgl::UniformBaseType::Uint:
602       return "UINT";
603   }
604   MOZ_CRASH("pacify gcc6 warning");
605 }
606 
607 const webgl::CachedDrawFetchLimits*
GetDrawFetchLimits() const608 webgl::LinkedProgramInfo::GetDrawFetchLimits() const {
609   const auto& webgl = prog->mContext;
610   const auto& vao = webgl->mBoundVertexArray;
611 
612   {
613     // We have to ensure that every enabled attrib array (not just the active
614     // ones) has a non-null buffer.
615     const auto badIndex = vao->GetAttribIsArrayWithNullBuffer();
616     if (badIndex) {
617       webgl->ErrorInvalidOperation(
618           "Vertex attrib array %u is enabled but"
619           " has no buffer bound.",
620           *badIndex);
621       return nullptr;
622     }
623   }
624 
625   const auto& activeAttribs = active.activeAttribs;
626 
627   webgl::CachedDrawFetchLimits fetchLimits;
628   fetchLimits.usedBuffers =
629       std::move(mScratchFetchLimits.usedBuffers);  // Avoid realloc.
630   fetchLimits.usedBuffers.clear();
631   fetchLimits.usedBuffers.reserve(activeAttribs.size());
632 
633   bool hasActiveAttrib = false;
634   bool hasActiveDivisor0 = false;
635 
636   for (const auto& progAttrib : activeAttribs) {
637     const auto& loc = progAttrib.location;
638     if (loc == -1) continue;
639     hasActiveAttrib |= true;
640 
641     const auto& binding = vao->AttribBinding(loc);
642     const auto& buffer = binding.buffer;
643     const auto& layout = binding.layout;
644     hasActiveDivisor0 |= (layout.divisor == 0);
645 
646     webgl::AttribBaseType attribDataBaseType;
647     if (layout.isArray) {
648       MOZ_ASSERT(buffer);
649       fetchLimits.usedBuffers.push_back(
650           {buffer.get(), static_cast<uint32_t>(loc)});
651 
652       attribDataBaseType = layout.baseType;
653 
654       const auto availBytes = buffer->ByteLength();
655       const auto availElems = AvailGroups(availBytes, layout.byteOffset,
656                                           layout.byteSize, layout.byteStride);
657       if (layout.divisor) {
658         const auto availInstances =
659             CheckedInt<uint64_t>(availElems) * layout.divisor;
660         if (availInstances.isValid()) {
661           fetchLimits.maxInstances =
662               std::min(fetchLimits.maxInstances, availInstances.value());
663         }  // If not valid, it overflowed too large, so we're super safe.
664       } else {
665         fetchLimits.maxVerts = std::min(fetchLimits.maxVerts, availElems);
666       }
667     } else {
668       attribDataBaseType = webgl->mGenericVertexAttribTypes[loc];
669     }
670 
671     const auto& progBaseType = progAttrib.baseType;
672     if ((attribDataBaseType != progBaseType) &&
673         (progBaseType != webgl::AttribBaseType::Boolean)) {
674       const auto& dataType = ToString(attribDataBaseType);
675       const auto& progType = ToString(progBaseType);
676       webgl->ErrorInvalidOperation(
677           "Vertex attrib %u requires data of type %s,"
678           " but is being supplied with type %s.",
679           loc, progType, dataType);
680       return nullptr;
681     }
682   }
683 
684   if (hasActiveAttrib && !hasActiveDivisor0) {
685     webgl->ErrorInvalidOperation(
686         "One active vertex attrib (if any are active)"
687         " must have a divisor of 0.");
688     return nullptr;
689   }
690 
691   // -
692 
693   mScratchFetchLimits = std::move(fetchLimits);
694   return &mScratchFetchLimits;
695 }
696 
697 ////////////////////////////////////////////////////////////////////////////////
698 // WebGLProgram
699 
WebGLProgram(WebGLContext * webgl)700 WebGLProgram::WebGLProgram(WebGLContext* webgl)
701     : WebGLContextBoundObject(webgl),
702       mGLName(webgl->gl->fCreateProgram()),
703       mNumActiveTFOs(0),
704       mNextLink_TransformFeedbackBufferMode(LOCAL_GL_INTERLEAVED_ATTRIBS) {}
705 
~WebGLProgram()706 WebGLProgram::~WebGLProgram() {
707   mVertShader = nullptr;
708   mFragShader = nullptr;
709 
710   mMostRecentLinkInfo = nullptr;
711 
712   if (!mContext) return;
713   mContext->gl->fDeleteProgram(mGLName);
714 }
715 
716 ////////////////////////////////////////////////////////////////////////////////
717 // GL funcs
718 
AttachShader(WebGLShader & shader)719 void WebGLProgram::AttachShader(WebGLShader& shader) {
720   RefPtr<WebGLShader>* shaderSlot = nullptr;
721   switch (shader.mType) {
722     case LOCAL_GL_VERTEX_SHADER:
723       shaderSlot = &mVertShader;
724       break;
725     case LOCAL_GL_FRAGMENT_SHADER:
726       shaderSlot = &mFragShader;
727       break;
728   }
729   MOZ_ASSERT(shaderSlot);
730 
731   *shaderSlot = &shader;
732 
733   mContext->gl->fAttachShader(mGLName, shader.mGLName);
734 }
735 
BindAttribLocation(GLuint loc,const std::string & name)736 void WebGLProgram::BindAttribLocation(GLuint loc, const std::string& name) {
737   const auto err = CheckGLSLVariableName(mContext->IsWebGL2(), name);
738   if (err) {
739     mContext->GenerateError(err->type, "%s", err->info.c_str());
740     return;
741   }
742 
743   if (loc >= mContext->MaxVertexAttribs()) {
744     mContext->ErrorInvalidValue(
745         "`location` must be less than"
746         " MAX_VERTEX_ATTRIBS.");
747     return;
748   }
749 
750   if (name.find("gl_") == 0) {
751     mContext->ErrorInvalidOperation(
752         "Can't set the location of a"
753         " name that starts with 'gl_'.");
754     return;
755   }
756 
757   auto res = mNextLink_BoundAttribLocs.insert({name, loc});
758 
759   const auto& wasInserted = res.second;
760   if (!wasInserted) {
761     const auto& itr = res.first;
762     itr->second = loc;
763   }
764 }
765 
DetachShader(const WebGLShader & shader)766 void WebGLProgram::DetachShader(const WebGLShader& shader) {
767   RefPtr<WebGLShader>* shaderSlot = nullptr;
768   switch (shader.mType) {
769     case LOCAL_GL_VERTEX_SHADER:
770       shaderSlot = &mVertShader;
771       break;
772     case LOCAL_GL_FRAGMENT_SHADER:
773       shaderSlot = &mFragShader;
774       break;
775   }
776   MOZ_ASSERT(shaderSlot);
777 
778   if (*shaderSlot != &shader) return;
779 
780   *shaderSlot = nullptr;
781 
782   mContext->gl->fDetachShader(mGLName, shader.mGLName);
783 }
784 
UniformBlockBinding(GLuint uniformBlockIndex,GLuint uniformBlockBinding) const785 void WebGLProgram::UniformBlockBinding(GLuint uniformBlockIndex,
786                                        GLuint uniformBlockBinding) const {
787   if (!IsLinked()) {
788     mContext->ErrorInvalidOperation("`program` must be linked.");
789     return;
790   }
791 
792   auto& uniformBlocks = LinkInfo()->uniformBlocks;
793   if (uniformBlockIndex >= uniformBlocks.size()) {
794     mContext->ErrorInvalidValue("Index %u invalid.", uniformBlockIndex);
795     return;
796   }
797   auto& uniformBlock = uniformBlocks[uniformBlockIndex];
798 
799   const auto& indexedBindings = mContext->mIndexedUniformBufferBindings;
800   if (uniformBlockBinding >= indexedBindings.size()) {
801     mContext->ErrorInvalidValue("Binding %u invalid.", uniformBlockBinding);
802     return;
803   }
804   const auto& indexedBinding = indexedBindings[uniformBlockBinding];
805 
806   ////
807 
808   gl::GLContext* gl = mContext->GL();
809   gl->fUniformBlockBinding(mGLName, uniformBlockIndex, uniformBlockBinding);
810 
811   ////
812 
813   uniformBlock.binding = &indexedBinding;
814 }
815 
ValidateForLink()816 bool WebGLProgram::ValidateForLink() {
817   if (!mVertShader || !mVertShader->IsCompiled()) {
818     mLinkLog = "Must have a compiled vertex shader attached.";
819     return false;
820   }
821   const auto& vertInfo = *mVertShader->CompileResults();
822 
823   if (!mFragShader || !mFragShader->IsCompiled()) {
824     mLinkLog = "Must have an compiled fragment shader attached.";
825     return false;
826   }
827   const auto& fragInfo = *mFragShader->CompileResults();
828 
829   nsCString errInfo;
830   if (!fragInfo.CanLinkTo(vertInfo, &errInfo)) {
831     mLinkLog = errInfo.BeginReading();
832     return false;
833   }
834 
835   const auto& gl = mContext->gl;
836 
837   if (gl->WorkAroundDriverBugs() && mContext->mIsMesa) {
838     // Bug 1203135: Mesa crashes internally if we exceed the reported maximum
839     // attribute count.
840     if (mVertShader->NumAttributes() > mContext->MaxVertexAttribs()) {
841       mLinkLog =
842           "Number of attributes exceeds Mesa's reported max"
843           " attribute count.";
844       return false;
845     }
846   }
847 
848   return true;
849 }
850 
LinkProgram()851 void WebGLProgram::LinkProgram() {
852   if (mNumActiveTFOs) {
853     mContext->ErrorInvalidOperation(
854         "Program is in-use by one or more active"
855         " transform feedback objects.");
856     return;
857   }
858 
859   // as some of the validation changes program state
860 
861   mLinkLog = {};
862   mMostRecentLinkInfo = nullptr;
863 
864   if (!ValidateForLink()) {
865     mContext->GenerateWarning("%s", mLinkLog.c_str());
866     return;
867   }
868 
869   // Bind the attrib locations.
870   // This can't be done trivially, because we have to deal with mapped attrib
871   // names.
872   for (const auto& pair : mNextLink_BoundAttribLocs) {
873     const auto& name = pair.first;
874     const auto& index = pair.second;
875 
876     mVertShader->BindAttribLocation(mGLName, name, index);
877   }
878 
879   // Storage for transform feedback varyings before link.
880   // (Work around for bug seen on nVidia drivers.)
881   std::vector<std::string> scopedMappedTFVaryings;
882 
883   if (mContext->IsWebGL2()) {
884     mVertShader->MapTransformFeedbackVaryings(
885         mNextLink_TransformFeedbackVaryings, &scopedMappedTFVaryings);
886 
887     std::vector<const char*> driverVaryings;
888     driverVaryings.reserve(scopedMappedTFVaryings.size());
889     for (const auto& cur : scopedMappedTFVaryings) {
890       driverVaryings.push_back(cur.c_str());
891     }
892 
893     mContext->gl->fTransformFeedbackVaryings(
894         mGLName, driverVaryings.size(), driverVaryings.data(),
895         mNextLink_TransformFeedbackBufferMode);
896   }
897 
898   LinkAndUpdate();
899 
900   if (mMostRecentLinkInfo) {
901     std::string postLinkLog;
902     if (ValidateAfterTentativeLink(&postLinkLog)) return;
903 
904     mMostRecentLinkInfo = nullptr;
905     mLinkLog = std::move(postLinkLog);
906   }
907 
908   // Failed link.
909   if (mContext->ShouldGenerateWarnings()) {
910     // report shader/program infoLogs as warnings.
911     // note that shader compilation errors can be deferred to linkProgram,
912     // which is why we can't do anything in compileShader. In practice we could
913     // report in compileShader the translation errors generated by ANGLE,
914     // but it seems saner to keep a single way of obtaining shader infologs.
915     if (!mLinkLog.empty()) {
916       mContext->GenerateWarning(
917           "Failed to link, leaving the following"
918           " log:\n%s\n",
919           mLinkLog.c_str());
920     }
921   }
922 }
923 
NumUsedLocationsByElemType(GLenum elemType)924 static uint8_t NumUsedLocationsByElemType(GLenum elemType) {
925   // GLES 3.0.4 p55
926 
927   switch (elemType) {
928     case LOCAL_GL_FLOAT_MAT2:
929     case LOCAL_GL_FLOAT_MAT2x3:
930     case LOCAL_GL_FLOAT_MAT2x4:
931       return 2;
932 
933     case LOCAL_GL_FLOAT_MAT3x2:
934     case LOCAL_GL_FLOAT_MAT3:
935     case LOCAL_GL_FLOAT_MAT3x4:
936       return 3;
937 
938     case LOCAL_GL_FLOAT_MAT4x2:
939     case LOCAL_GL_FLOAT_MAT4x3:
940     case LOCAL_GL_FLOAT_MAT4:
941       return 4;
942 
943     default:
944       return 1;
945   }
946 }
947 
ElemTypeComponents(const GLenum elemType)948 uint8_t ElemTypeComponents(const GLenum elemType) {
949   switch (elemType) {
950     case LOCAL_GL_BOOL:
951     case LOCAL_GL_FLOAT:
952     case LOCAL_GL_INT:
953     case LOCAL_GL_UNSIGNED_INT:
954     case LOCAL_GL_SAMPLER_2D:
955     case LOCAL_GL_SAMPLER_3D:
956     case LOCAL_GL_SAMPLER_CUBE:
957     case LOCAL_GL_SAMPLER_2D_SHADOW:
958     case LOCAL_GL_SAMPLER_2D_ARRAY:
959     case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
960     case LOCAL_GL_SAMPLER_CUBE_SHADOW:
961     case LOCAL_GL_INT_SAMPLER_2D:
962     case LOCAL_GL_INT_SAMPLER_3D:
963     case LOCAL_GL_INT_SAMPLER_CUBE:
964     case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
965     case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
966     case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
967     case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
968     case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
969       return 1;
970 
971     case LOCAL_GL_BOOL_VEC2:
972     case LOCAL_GL_FLOAT_VEC2:
973     case LOCAL_GL_INT_VEC2:
974     case LOCAL_GL_UNSIGNED_INT_VEC2:
975       return 2;
976 
977     case LOCAL_GL_BOOL_VEC3:
978     case LOCAL_GL_FLOAT_VEC3:
979     case LOCAL_GL_INT_VEC3:
980     case LOCAL_GL_UNSIGNED_INT_VEC3:
981       return 3;
982 
983     case LOCAL_GL_BOOL_VEC4:
984     case LOCAL_GL_FLOAT_VEC4:
985     case LOCAL_GL_INT_VEC4:
986     case LOCAL_GL_UNSIGNED_INT_VEC4:
987     case LOCAL_GL_FLOAT_MAT2:
988       return 4;
989 
990     case LOCAL_GL_FLOAT_MAT2x3:
991     case LOCAL_GL_FLOAT_MAT3x2:
992       return 2 * 3;
993 
994     case LOCAL_GL_FLOAT_MAT2x4:
995     case LOCAL_GL_FLOAT_MAT4x2:
996       return 2 * 4;
997 
998     case LOCAL_GL_FLOAT_MAT3:
999       return 3 * 3;
1000 
1001     case LOCAL_GL_FLOAT_MAT3x4:
1002     case LOCAL_GL_FLOAT_MAT4x3:
1003       return 3 * 4;
1004 
1005     case LOCAL_GL_FLOAT_MAT4:
1006       return 4 * 4;
1007 
1008     default:
1009       return 0;
1010   }
1011 }
1012 
ValidateAfterTentativeLink(std::string * const out_linkLog) const1013 bool WebGLProgram::ValidateAfterTentativeLink(
1014     std::string* const out_linkLog) const {
1015   const auto& linkInfo = mMostRecentLinkInfo;
1016   const auto& gl = mContext->gl;
1017 
1018   // Check if the attrib name conflicting to uniform name
1019   {
1020     std::unordered_set<std::string> attribNames;
1021     for (const auto& attrib : linkInfo->active.activeAttribs) {
1022       attribNames.insert(attrib.name);
1023     }
1024     for (const auto& uniform : linkInfo->active.activeUniforms) {
1025       auto name = uniform.name;
1026       const auto maybe = webgl::ParseIndexed(name);
1027       if (maybe) {
1028         name = maybe->name;
1029       }
1030       if (attribNames.count(name)) {
1031         *out_linkLog = nsPrintfCString(
1032                            "Attrib name conflicts with uniform name:"
1033                            " %s",
1034                            name.c_str())
1035                            .BeginReading();
1036         return false;
1037       }
1038     }
1039   }
1040   {
1041     std::unordered_map<uint32_t, const std::string&> nameByLoc;
1042     for (const auto& attrib : linkInfo->active.activeAttribs) {
1043       if (attrib.location == -1) continue;
1044 
1045       const auto& elemType = attrib.elemType;
1046       const auto numUsedLocs = NumUsedLocationsByElemType(elemType);
1047       for (uint32_t i = 0; i < numUsedLocs; i++) {
1048         const uint32_t usedLoc = attrib.location + i;
1049 
1050         const auto res = nameByLoc.insert({usedLoc, attrib.name});
1051         const bool& didInsert = res.second;
1052         if (!didInsert) {
1053           const auto& aliasingName = attrib.name;
1054           const auto& itrExisting = res.first;
1055           const auto& existingName = itrExisting->second;
1056           *out_linkLog = nsPrintfCString(
1057                              "Attrib \"%s\" aliases locations used by"
1058                              " attrib \"%s\".",
1059                              aliasingName.c_str(), existingName.c_str())
1060                              .BeginReading();
1061           return false;
1062         }
1063       }
1064     }
1065   }
1066 
1067   // Forbid too many components for specified buffer mode
1068   const auto& activeTfVaryings = linkInfo->active.activeTfVaryings;
1069   MOZ_ASSERT(mNextLink_TransformFeedbackVaryings.size() ==
1070              activeTfVaryings.size());
1071   if (!activeTfVaryings.empty()) {
1072     GLuint maxComponentsPerIndex = 0;
1073     switch (linkInfo->transformFeedbackBufferMode) {
1074       case LOCAL_GL_INTERLEAVED_ATTRIBS:
1075         gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS,
1076                          &maxComponentsPerIndex);
1077         break;
1078 
1079       case LOCAL_GL_SEPARATE_ATTRIBS:
1080         gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS,
1081                          &maxComponentsPerIndex);
1082         break;
1083 
1084       default:
1085         MOZ_CRASH("`bufferMode`");
1086     }
1087 
1088     std::vector<size_t> componentsPerVert;
1089     for (const auto& cur : activeTfVaryings) {
1090       if (componentsPerVert.empty() ||
1091           linkInfo->transformFeedbackBufferMode == LOCAL_GL_SEPARATE_ATTRIBS) {
1092         componentsPerVert.push_back(0);
1093       }
1094 
1095       size_t varyingComponents = ElemTypeComponents(cur.elemType);
1096       MOZ_ASSERT(varyingComponents);
1097       varyingComponents *= cur.elemCount;
1098 
1099       auto& totalComponentsForIndex = *(componentsPerVert.rbegin());
1100       totalComponentsForIndex += varyingComponents;
1101 
1102       if (totalComponentsForIndex > maxComponentsPerIndex) {
1103         *out_linkLog = nsPrintfCString(
1104                            "Transform feedback varying \"%s\""
1105                            " pushed `componentsForIndex` over the"
1106                            " limit of %u.",
1107                            cur.name.c_str(), maxComponentsPerIndex)
1108                            .BeginReading();
1109         return false;
1110       }
1111     }
1112 
1113     linkInfo->componentsPerTFVert = std::move(componentsPerVert);
1114   }
1115 
1116   return true;
1117 }
1118 
UseProgram() const1119 bool WebGLProgram::UseProgram() const {
1120   if (!mMostRecentLinkInfo) {
1121     mContext->ErrorInvalidOperation(
1122         "Program has not been successfully linked.");
1123     return false;
1124   }
1125 
1126   if (mContext->mBoundTransformFeedback &&
1127       mContext->mBoundTransformFeedback->mIsActive &&
1128       !mContext->mBoundTransformFeedback->mIsPaused) {
1129     mContext->ErrorInvalidOperation(
1130         "Transform feedback active and not paused.");
1131     return false;
1132   }
1133 
1134   mContext->gl->fUseProgram(mGLName);
1135   return true;
1136 }
1137 
ValidateProgram() const1138 bool WebGLProgram::ValidateProgram() const {
1139   gl::GLContext* gl = mContext->gl;
1140 
1141 #ifdef XP_MACOSX
1142   // See bug 593867 for NVIDIA and bug 657201 for ATI. The latter is confirmed
1143   // with Mac OS 10.6.7.
1144   if (gl->WorkAroundDriverBugs()) {
1145     mContext->GenerateWarning(
1146         "Implemented as a no-op on"
1147         " Mac to work around crashes.");
1148     return true;
1149   }
1150 #endif
1151 
1152   gl->fValidateProgram(mGLName);
1153   GLint ok = 0;
1154   gl->fGetProgramiv(mGLName, LOCAL_GL_VALIDATE_STATUS, &ok);
1155   return bool(ok);
1156 }
1157 
1158 ////////////////////////////////////////////////////////////////////////////////
1159 
LinkAndUpdate()1160 void WebGLProgram::LinkAndUpdate() {
1161   mMostRecentLinkInfo = nullptr;
1162 
1163   gl::GLContext* gl = mContext->gl;
1164   gl->fLinkProgram(mGLName);
1165 
1166   // Grab the program log.
1167   {
1168     GLuint logLenWithNull = 0;
1169     gl->fGetProgramiv(mGLName, LOCAL_GL_INFO_LOG_LENGTH,
1170                       (GLint*)&logLenWithNull);
1171     if (logLenWithNull > 1) {
1172       std::vector<char> buffer(logLenWithNull);
1173       gl->fGetProgramInfoLog(mGLName, buffer.size(), nullptr, buffer.data());
1174       mLinkLog = buffer.data();
1175     } else {
1176       mLinkLog.clear();
1177     }
1178   }
1179 
1180   GLint ok = 0;
1181   gl->fGetProgramiv(mGLName, LOCAL_GL_LINK_STATUS, &ok);
1182   if (!ok) return;
1183 
1184   mMostRecentLinkInfo =
1185       QueryProgramInfo(this, gl);  // Fallible after context loss.
1186 }
1187 
TransformFeedbackVaryings(const std::vector<std::string> & varyings,GLenum bufferMode)1188 void WebGLProgram::TransformFeedbackVaryings(
1189     const std::vector<std::string>& varyings, GLenum bufferMode) {
1190   const auto& gl = mContext->gl;
1191 
1192   switch (bufferMode) {
1193     case LOCAL_GL_INTERLEAVED_ATTRIBS:
1194       break;
1195 
1196     case LOCAL_GL_SEPARATE_ATTRIBS: {
1197       GLuint maxAttribs = 0;
1198       gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS,
1199                        &maxAttribs);
1200       if (varyings.size() > maxAttribs) {
1201         mContext->ErrorInvalidValue("Length of `varyings` exceeds %s.",
1202                                     "TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS");
1203         return;
1204       }
1205     } break;
1206 
1207     default:
1208       mContext->ErrorInvalidEnumInfo("bufferMode", bufferMode);
1209       return;
1210   }
1211 
1212   ////
1213 
1214   mNextLink_TransformFeedbackVaryings = varyings;
1215   mNextLink_TransformFeedbackBufferMode = bufferMode;
1216 }
1217 
1218 }  // namespace mozilla
1219