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