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->fragOutputs.insert({x.loc, x});
352     };
353 
354     if (version == 300) {
355       for (const auto& cur : compileResults->mOutputVariables) {
356         auto loc = cur.location;
357         if (loc == -1) loc = 0;
358 
359         const auto info =
360             webgl::FragOutputInfo{uint8_t(loc), cur.name, cur.mappedName,
361                                   FragOutputBaseType(cur.type)};
362         if (!cur.isArray()) {
363           fnAddInfo(info);
364           continue;
365         }
366         MOZ_ASSERT(cur.arraySizes.size() == 1);
367         for (uint32_t i = 0; i < cur.arraySizes[0]; ++i) {
368           const auto indexStr = std::string("[") + std::to_string(i) + "]";
369 
370           const auto userName = info.userName + indexStr;
371           const auto mappedName = info.mappedName + indexStr;
372 
373           const auto indexedInfo = webgl::FragOutputInfo{
374               uint8_t(info.loc + i), userName, mappedName, info.baseType};
375           fnAddInfo(indexedInfo);
376         }
377       }
378     } else {
379       // ANGLE's translator doesn't tell us about non-user frag outputs. :(
380 
381       const auto& translatedSource = compileResults->mObjectCode;
382       uint32_t drawBuffers = 1;
383       if (translatedSource.find("(gl_FragData[1]") != std::string::npos ||
384           translatedSource.find("(webgl_FragData[1]") != std::string::npos) {
385         // The matching with the leading '(' prevents cleverly-named user vars
386         // breaking this. Since ANGLE initializes all outputs, if this is an MRT
387         // shader, FragData[1] will be present. FragData[0] is valid for non-MRT
388         // shaders.
389         drawBuffers = webgl->GLMaxDrawBuffers();
390       } else if (translatedSource.find("(gl_FragColor") == std::string::npos &&
391                  translatedSource.find("(webgl_FragColor") ==
392                      std::string::npos &&
393                  translatedSource.find("(gl_FragData") == std::string::npos &&
394                  translatedSource.find("(webgl_FragData") ==
395                      std::string::npos) {
396         // We have to support no-color-output shaders?
397         drawBuffers = 0;
398       }
399 
400       for (uint32_t i = 0; i < drawBuffers; ++i) {
401         const auto name = std::string("gl_FragData[") + std::to_string(i) + "]";
402         const auto info = webgl::FragOutputInfo{uint8_t(i), name, name,
403                                                 webgl::TextureBaseType::Float};
404         fnAddInfo(info);
405       }
406     }
407   }
408 
409   const auto& vertShader = prog->VertShader();
410   const auto& vertCompileResults = vertShader->CompileResults();
411   const auto numViews = vertCompileResults->mVertexShaderNumViews;
412   if (numViews != -1) {
413     info->zLayerCount = AssertedCast<uint8_t>(numViews);
414   }
415 
416   // -
417 
418   auto& nameMap = info->nameMap;
419 
420   const auto fnAccum = [&](WebGLShader& shader) {
421     const auto& compRes = shader.CompileResults();
422     for (const auto& pair : compRes->mNameMap) {
423       nameMap.insert(pair);
424     }
425   };
426   fnAccum(*prog->VertShader());
427   fnAccum(*prog->FragShader());
428 
429   // -
430 
431   std::unordered_map<std::string, std::string> nameUnmap;
432   for (const auto& pair : nameMap) {
433     nameUnmap.insert({pair.second, pair.first});
434   }
435 
436   info->active =
437       GetLinkActiveInfo(*gl, prog->mGLName, webgl->IsWebGL2(), nameUnmap);
438 
439   // -
440 
441   for (const auto& attrib : info->active.activeAttribs) {
442     if (attrib.location == 0) {
443       info->attrib0Active = true;
444       break;
445     }
446   }
447 
448   // -
449 
450   for (const auto& uniform : info->active.activeUniforms) {
451     const auto& elemType = uniform.elemType;
452     webgl::SamplerUniformInfo* samplerInfo = nullptr;
453     const auto baseType = SamplerBaseType(elemType);
454     if (baseType) {
455       const bool isShadowSampler = IsShadowSampler(elemType);
456 
457       auto* texList = &webgl->mBound2DTextures;
458 
459       switch (elemType) {
460         case LOCAL_GL_SAMPLER_2D:
461         case LOCAL_GL_SAMPLER_2D_SHADOW:
462         case LOCAL_GL_INT_SAMPLER_2D:
463         case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
464           break;
465 
466         case LOCAL_GL_SAMPLER_CUBE:
467         case LOCAL_GL_SAMPLER_CUBE_SHADOW:
468         case LOCAL_GL_INT_SAMPLER_CUBE:
469         case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
470           texList = &webgl->mBoundCubeMapTextures;
471           break;
472 
473         case LOCAL_GL_SAMPLER_3D:
474         case LOCAL_GL_INT_SAMPLER_3D:
475         case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
476           texList = &webgl->mBound3DTextures;
477           break;
478 
479         case LOCAL_GL_SAMPLER_2D_ARRAY:
480         case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
481         case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
482         case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
483           texList = &webgl->mBound2DArrayTextures;
484           break;
485       }
486 
487       auto curInfo = std::unique_ptr<webgl::SamplerUniformInfo>(
488           new webgl::SamplerUniformInfo{*texList, *baseType, isShadowSampler});
489       curInfo->texUnits.resize(uniform.elemCount);
490       samplerInfo = curInfo.get();
491       info->samplerUniforms.push_back(std::move(curInfo));
492     }
493 
494     const auto valInfo = webgl::ActiveUniformValidationInfo::Make(uniform);
495 
496     for (const auto& pair : uniform.locByIndex) {
497       info->locationMap.insert(
498           {pair.second, {valInfo, pair.first, samplerInfo}});
499     }
500   }
501 
502   // -
503 
504   {
505     const auto& activeBlocks = info->active.activeUniformBlocks;
506     info->uniformBlocks.reserve(activeBlocks.size());
507     for (const auto& cur : activeBlocks) {
508       const auto curInfo = webgl::UniformBlockInfo{
509           cur, &webgl->mIndexedUniformBufferBindings[0]};
510       info->uniformBlocks.push_back(curInfo);
511     }
512   }
513 
514   return info;
515 }
516 
517 ////////////////////////////////////////////////////////////////////////////////
518 
LinkedProgramInfo(WebGLProgram * prog)519 webgl::LinkedProgramInfo::LinkedProgramInfo(WebGLProgram* prog)
520     : prog(prog),
521       transformFeedbackBufferMode(prog->mNextLink_TransformFeedbackBufferMode) {
522 }
523 
524 webgl::LinkedProgramInfo::~LinkedProgramInfo() = default;
525 
ToAttribBaseType(const GLenum elemType)526 webgl::AttribBaseType webgl::ToAttribBaseType(const GLenum elemType) {
527   switch (elemType) {
528     case LOCAL_GL_BOOL:
529     case LOCAL_GL_BOOL_VEC2:
530     case LOCAL_GL_BOOL_VEC3:
531     case LOCAL_GL_BOOL_VEC4:
532       return webgl::AttribBaseType::Boolean;
533 
534     case LOCAL_GL_FLOAT:
535     case LOCAL_GL_FLOAT_VEC2:
536     case LOCAL_GL_FLOAT_VEC3:
537     case LOCAL_GL_FLOAT_VEC4:
538     case LOCAL_GL_FLOAT_MAT2:
539     case LOCAL_GL_FLOAT_MAT2x3:
540     case LOCAL_GL_FLOAT_MAT3x2:
541     case LOCAL_GL_FLOAT_MAT2x4:
542     case LOCAL_GL_FLOAT_MAT4x2:
543     case LOCAL_GL_FLOAT_MAT3:
544     case LOCAL_GL_FLOAT_MAT3x4:
545     case LOCAL_GL_FLOAT_MAT4x3:
546     case LOCAL_GL_FLOAT_MAT4:
547       return webgl::AttribBaseType::Float;
548 
549     case LOCAL_GL_INT:
550     case LOCAL_GL_INT_VEC2:
551     case LOCAL_GL_INT_VEC3:
552     case LOCAL_GL_INT_VEC4:
553     case LOCAL_GL_SAMPLER_2D:
554     case LOCAL_GL_SAMPLER_3D:
555     case LOCAL_GL_SAMPLER_CUBE:
556     case LOCAL_GL_SAMPLER_2D_SHADOW:
557     case LOCAL_GL_SAMPLER_2D_ARRAY:
558     case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
559     case LOCAL_GL_SAMPLER_CUBE_SHADOW:
560     case LOCAL_GL_INT_SAMPLER_2D:
561     case LOCAL_GL_INT_SAMPLER_3D:
562     case LOCAL_GL_INT_SAMPLER_CUBE:
563     case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
564     case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
565     case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
566     case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
567     case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
568       return webgl::AttribBaseType::Int;
569 
570     case LOCAL_GL_UNSIGNED_INT:
571     case LOCAL_GL_UNSIGNED_INT_VEC2:
572     case LOCAL_GL_UNSIGNED_INT_VEC3:
573     case LOCAL_GL_UNSIGNED_INT_VEC4:
574       return webgl::AttribBaseType::Uint;
575 
576     default:
577       gfxCriticalError() << "Bad `elemType`: " << EnumString(elemType);
578       MOZ_CRASH("`elemType`");
579   }
580 }
581 
ToString(const webgl::AttribBaseType x)582 const char* webgl::ToString(const webgl::AttribBaseType x) {
583   switch (x) {
584     case webgl::AttribBaseType::Float:
585       return "FLOAT";
586     case webgl::AttribBaseType::Int:
587       return "INT";
588     case webgl::AttribBaseType::Uint:
589       return "UINT";
590     case webgl::AttribBaseType::Boolean:
591       return "BOOL";
592   }
593   MOZ_CRASH("pacify gcc6 warning");
594 }
595 
ToString(const webgl::UniformBaseType x)596 const char* webgl::ToString(const webgl::UniformBaseType x) {
597   switch (x) {
598     case webgl::UniformBaseType::Float:
599       return "FLOAT";
600     case webgl::UniformBaseType::Int:
601       return "INT";
602     case webgl::UniformBaseType::Uint:
603       return "UINT";
604   }
605   MOZ_CRASH("pacify gcc6 warning");
606 }
607 
608 const webgl::CachedDrawFetchLimits*
GetDrawFetchLimits() const609 webgl::LinkedProgramInfo::GetDrawFetchLimits() const {
610   const auto& webgl = prog->mContext;
611   const auto& vao = webgl->mBoundVertexArray;
612 
613   {
614     // We have to ensure that every enabled attrib array (not just the active
615     // ones) has a non-null buffer.
616     const auto badIndex = vao->GetAttribIsArrayWithNullBuffer();
617     if (badIndex) {
618       webgl->ErrorInvalidOperation(
619           "Vertex attrib array %u is enabled but"
620           " has no buffer bound.",
621           *badIndex);
622       return nullptr;
623     }
624   }
625 
626   const auto& activeAttribs = active.activeAttribs;
627 
628   webgl::CachedDrawFetchLimits fetchLimits;
629   fetchLimits.usedBuffers =
630       std::move(mScratchFetchLimits.usedBuffers);  // Avoid realloc.
631   fetchLimits.usedBuffers.clear();
632   fetchLimits.usedBuffers.reserve(activeAttribs.size());
633 
634   bool hasActiveAttrib = false;
635   bool hasActiveDivisor0 = false;
636 
637   for (const auto& progAttrib : activeAttribs) {
638     const auto& loc = progAttrib.location;
639     if (loc == -1) continue;
640     hasActiveAttrib |= true;
641 
642     const auto& binding = vao->AttribBinding(loc);
643     const auto& buffer = binding.buffer;
644     const auto& layout = binding.layout;
645     hasActiveDivisor0 |= (layout.divisor == 0);
646 
647     webgl::AttribBaseType attribDataBaseType;
648     if (layout.isArray) {
649       MOZ_ASSERT(buffer);
650       fetchLimits.usedBuffers.push_back(
651           {buffer.get(), static_cast<uint32_t>(loc)});
652 
653       attribDataBaseType = layout.baseType;
654 
655       const auto availBytes = buffer->ByteLength();
656       const auto availElems = AvailGroups(availBytes, layout.byteOffset,
657                                           layout.byteSize, layout.byteStride);
658       if (layout.divisor) {
659         const auto availInstances =
660             CheckedInt<uint64_t>(availElems) * layout.divisor;
661         if (availInstances.isValid()) {
662           fetchLimits.maxInstances =
663               std::min(fetchLimits.maxInstances, availInstances.value());
664         }  // If not valid, it overflowed too large, so we're super safe.
665       } else {
666         fetchLimits.maxVerts = std::min(fetchLimits.maxVerts, availElems);
667       }
668     } else {
669       attribDataBaseType = webgl->mGenericVertexAttribTypes[loc];
670     }
671 
672     const auto& progBaseType = progAttrib.baseType;
673     if ((attribDataBaseType != progBaseType) &&
674         (progBaseType != webgl::AttribBaseType::Boolean)) {
675       const auto& dataType = ToString(attribDataBaseType);
676       const auto& progType = ToString(progBaseType);
677       webgl->ErrorInvalidOperation(
678           "Vertex attrib %u requires data of type %s,"
679           " but is being supplied with type %s.",
680           loc, progType, dataType);
681       return nullptr;
682     }
683   }
684 
685   if (!webgl->IsWebGL2() && hasActiveAttrib && !hasActiveDivisor0) {
686     webgl->ErrorInvalidOperation(
687         "One active vertex attrib (if any are active)"
688         " must have a divisor of 0.");
689     return nullptr;
690   }
691 
692   // -
693 
694   mScratchFetchLimits = std::move(fetchLimits);
695   return &mScratchFetchLimits;
696 }
697 
698 ////////////////////////////////////////////////////////////////////////////////
699 // WebGLProgram
700 
WebGLProgram(WebGLContext * webgl)701 WebGLProgram::WebGLProgram(WebGLContext* webgl)
702     : WebGLContextBoundObject(webgl),
703       mGLName(webgl->gl->fCreateProgram()),
704       mNumActiveTFOs(0),
705       mNextLink_TransformFeedbackBufferMode(LOCAL_GL_INTERLEAVED_ATTRIBS) {}
706 
~WebGLProgram()707 WebGLProgram::~WebGLProgram() {
708   mVertShader = nullptr;
709   mFragShader = nullptr;
710 
711   mMostRecentLinkInfo = nullptr;
712 
713   if (!mContext) return;
714   mContext->gl->fDeleteProgram(mGLName);
715 }
716 
717 ////////////////////////////////////////////////////////////////////////////////
718 // GL funcs
719 
AttachShader(WebGLShader & shader)720 void WebGLProgram::AttachShader(WebGLShader& shader) {
721   RefPtr<WebGLShader>* shaderSlot = nullptr;
722   switch (shader.mType) {
723     case LOCAL_GL_VERTEX_SHADER:
724       shaderSlot = &mVertShader;
725       break;
726     case LOCAL_GL_FRAGMENT_SHADER:
727       shaderSlot = &mFragShader;
728       break;
729   }
730   MOZ_ASSERT(shaderSlot);
731 
732   *shaderSlot = &shader;
733 
734   mContext->gl->fAttachShader(mGLName, shader.mGLName);
735 }
736 
BindAttribLocation(GLuint loc,const std::string & name)737 void WebGLProgram::BindAttribLocation(GLuint loc, const std::string& name) {
738   const auto err = CheckGLSLVariableName(mContext->IsWebGL2(), name);
739   if (err) {
740     mContext->GenerateError(err->type, "%s", err->info.c_str());
741     return;
742   }
743 
744   if (loc >= mContext->MaxVertexAttribs()) {
745     mContext->ErrorInvalidValue(
746         "`location` must be less than"
747         " MAX_VERTEX_ATTRIBS.");
748     return;
749   }
750 
751   if (name.find("gl_") == 0) {
752     mContext->ErrorInvalidOperation(
753         "Can't set the location of a"
754         " name that starts with 'gl_'.");
755     return;
756   }
757 
758   auto res = mNextLink_BoundAttribLocs.insert({name, loc});
759 
760   const auto& wasInserted = res.second;
761   if (!wasInserted) {
762     const auto& itr = res.first;
763     itr->second = loc;
764   }
765 }
766 
DetachShader(const WebGLShader & shader)767 void WebGLProgram::DetachShader(const WebGLShader& shader) {
768   RefPtr<WebGLShader>* shaderSlot = nullptr;
769   switch (shader.mType) {
770     case LOCAL_GL_VERTEX_SHADER:
771       shaderSlot = &mVertShader;
772       break;
773     case LOCAL_GL_FRAGMENT_SHADER:
774       shaderSlot = &mFragShader;
775       break;
776   }
777   MOZ_ASSERT(shaderSlot);
778 
779   if (*shaderSlot != &shader) return;
780 
781   *shaderSlot = nullptr;
782 
783   mContext->gl->fDetachShader(mGLName, shader.mGLName);
784 }
785 
UniformBlockBinding(GLuint uniformBlockIndex,GLuint uniformBlockBinding) const786 void WebGLProgram::UniformBlockBinding(GLuint uniformBlockIndex,
787                                        GLuint uniformBlockBinding) const {
788   if (!IsLinked()) {
789     mContext->ErrorInvalidOperation("`program` must be linked.");
790     return;
791   }
792 
793   auto& uniformBlocks = LinkInfo()->uniformBlocks;
794   if (uniformBlockIndex >= uniformBlocks.size()) {
795     mContext->ErrorInvalidValue("Index %u invalid.", uniformBlockIndex);
796     return;
797   }
798   auto& uniformBlock = uniformBlocks[uniformBlockIndex];
799 
800   const auto& indexedBindings = mContext->mIndexedUniformBufferBindings;
801   if (uniformBlockBinding >= indexedBindings.size()) {
802     mContext->ErrorInvalidValue("Binding %u invalid.", uniformBlockBinding);
803     return;
804   }
805   const auto& indexedBinding = indexedBindings[uniformBlockBinding];
806 
807   ////
808 
809   gl::GLContext* gl = mContext->GL();
810   gl->fUniformBlockBinding(mGLName, uniformBlockIndex, uniformBlockBinding);
811 
812   ////
813 
814   uniformBlock.binding = &indexedBinding;
815 }
816 
ValidateForLink()817 bool WebGLProgram::ValidateForLink() {
818   if (!mVertShader || !mVertShader->IsCompiled()) {
819     mLinkLog = "Must have a compiled vertex shader attached.";
820     return false;
821   }
822   const auto& vertInfo = *mVertShader->CompileResults();
823 
824   if (!mFragShader || !mFragShader->IsCompiled()) {
825     mLinkLog = "Must have an compiled fragment shader attached.";
826     return false;
827   }
828   const auto& fragInfo = *mFragShader->CompileResults();
829 
830   nsCString errInfo;
831   if (!fragInfo.CanLinkTo(vertInfo, &errInfo)) {
832     mLinkLog = errInfo.BeginReading();
833     return false;
834   }
835 
836   const auto& gl = mContext->gl;
837 
838   if (gl->WorkAroundDriverBugs() && mContext->mIsMesa) {
839     // Bug 1203135: Mesa crashes internally if we exceed the reported maximum
840     // attribute count.
841     if (mVertShader->NumAttributes() > mContext->MaxVertexAttribs()) {
842       mLinkLog =
843           "Number of attributes exceeds Mesa's reported max"
844           " attribute count.";
845       return false;
846     }
847   }
848 
849   return true;
850 }
851 
LinkProgram()852 void WebGLProgram::LinkProgram() {
853   if (mNumActiveTFOs) {
854     mContext->ErrorInvalidOperation(
855         "Program is in-use by one or more active"
856         " transform feedback objects.");
857     return;
858   }
859 
860   // as some of the validation changes program state
861 
862   mLinkLog = {};
863   mMostRecentLinkInfo = nullptr;
864 
865   if (!ValidateForLink()) {
866     mContext->GenerateWarning("%s", mLinkLog.c_str());
867     return;
868   }
869 
870   // Bind the attrib locations.
871   // This can't be done trivially, because we have to deal with mapped attrib
872   // names.
873   for (const auto& pair : mNextLink_BoundAttribLocs) {
874     const auto& name = pair.first;
875     const auto& index = pair.second;
876 
877     mVertShader->BindAttribLocation(mGLName, name, index);
878   }
879 
880   // Storage for transform feedback varyings before link.
881   // (Work around for bug seen on nVidia drivers.)
882   std::vector<std::string> scopedMappedTFVaryings;
883 
884   if (mContext->IsWebGL2()) {
885     mVertShader->MapTransformFeedbackVaryings(
886         mNextLink_TransformFeedbackVaryings, &scopedMappedTFVaryings);
887 
888     std::vector<const char*> driverVaryings;
889     driverVaryings.reserve(scopedMappedTFVaryings.size());
890     for (const auto& cur : scopedMappedTFVaryings) {
891       driverVaryings.push_back(cur.c_str());
892     }
893 
894     mContext->gl->fTransformFeedbackVaryings(
895         mGLName, driverVaryings.size(), driverVaryings.data(),
896         mNextLink_TransformFeedbackBufferMode);
897   }
898 
899   LinkAndUpdate();
900 
901   if (mMostRecentLinkInfo) {
902     std::string postLinkLog;
903     if (ValidateAfterTentativeLink(&postLinkLog)) return;
904 
905     mMostRecentLinkInfo = nullptr;
906     mLinkLog = std::move(postLinkLog);
907   }
908 
909   // Failed link.
910   if (mContext->ShouldGenerateWarnings()) {
911     // report shader/program infoLogs as warnings.
912     // note that shader compilation errors can be deferred to linkProgram,
913     // which is why we can't do anything in compileShader. In practice we could
914     // report in compileShader the translation errors generated by ANGLE,
915     // but it seems saner to keep a single way of obtaining shader infologs.
916     if (!mLinkLog.empty()) {
917       mContext->GenerateWarning(
918           "Failed to link, leaving the following"
919           " log:\n%s\n",
920           mLinkLog.c_str());
921     }
922   }
923 }
924 
NumUsedLocationsByElemType(GLenum elemType)925 static uint8_t NumUsedLocationsByElemType(GLenum elemType) {
926   // GLES 3.0.4 p55
927 
928   switch (elemType) {
929     case LOCAL_GL_FLOAT_MAT2:
930     case LOCAL_GL_FLOAT_MAT2x3:
931     case LOCAL_GL_FLOAT_MAT2x4:
932       return 2;
933 
934     case LOCAL_GL_FLOAT_MAT3x2:
935     case LOCAL_GL_FLOAT_MAT3:
936     case LOCAL_GL_FLOAT_MAT3x4:
937       return 3;
938 
939     case LOCAL_GL_FLOAT_MAT4x2:
940     case LOCAL_GL_FLOAT_MAT4x3:
941     case LOCAL_GL_FLOAT_MAT4:
942       return 4;
943 
944     default:
945       return 1;
946   }
947 }
948 
ElemTypeComponents(const GLenum elemType)949 uint8_t ElemTypeComponents(const GLenum elemType) {
950   switch (elemType) {
951     case LOCAL_GL_BOOL:
952     case LOCAL_GL_FLOAT:
953     case LOCAL_GL_INT:
954     case LOCAL_GL_UNSIGNED_INT:
955     case LOCAL_GL_SAMPLER_2D:
956     case LOCAL_GL_SAMPLER_3D:
957     case LOCAL_GL_SAMPLER_CUBE:
958     case LOCAL_GL_SAMPLER_2D_SHADOW:
959     case LOCAL_GL_SAMPLER_2D_ARRAY:
960     case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
961     case LOCAL_GL_SAMPLER_CUBE_SHADOW:
962     case LOCAL_GL_INT_SAMPLER_2D:
963     case LOCAL_GL_INT_SAMPLER_3D:
964     case LOCAL_GL_INT_SAMPLER_CUBE:
965     case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
966     case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
967     case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
968     case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
969     case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
970       return 1;
971 
972     case LOCAL_GL_BOOL_VEC2:
973     case LOCAL_GL_FLOAT_VEC2:
974     case LOCAL_GL_INT_VEC2:
975     case LOCAL_GL_UNSIGNED_INT_VEC2:
976       return 2;
977 
978     case LOCAL_GL_BOOL_VEC3:
979     case LOCAL_GL_FLOAT_VEC3:
980     case LOCAL_GL_INT_VEC3:
981     case LOCAL_GL_UNSIGNED_INT_VEC3:
982       return 3;
983 
984     case LOCAL_GL_BOOL_VEC4:
985     case LOCAL_GL_FLOAT_VEC4:
986     case LOCAL_GL_INT_VEC4:
987     case LOCAL_GL_UNSIGNED_INT_VEC4:
988     case LOCAL_GL_FLOAT_MAT2:
989       return 4;
990 
991     case LOCAL_GL_FLOAT_MAT2x3:
992     case LOCAL_GL_FLOAT_MAT3x2:
993       return 2 * 3;
994 
995     case LOCAL_GL_FLOAT_MAT2x4:
996     case LOCAL_GL_FLOAT_MAT4x2:
997       return 2 * 4;
998 
999     case LOCAL_GL_FLOAT_MAT3:
1000       return 3 * 3;
1001 
1002     case LOCAL_GL_FLOAT_MAT3x4:
1003     case LOCAL_GL_FLOAT_MAT4x3:
1004       return 3 * 4;
1005 
1006     case LOCAL_GL_FLOAT_MAT4:
1007       return 4 * 4;
1008 
1009     default:
1010       return 0;
1011   }
1012 }
1013 
ValidateAfterTentativeLink(std::string * const out_linkLog) const1014 bool WebGLProgram::ValidateAfterTentativeLink(
1015     std::string* const out_linkLog) const {
1016   const auto& linkInfo = mMostRecentLinkInfo;
1017   const auto& gl = mContext->gl;
1018 
1019   // Check if the attrib name conflicting to uniform name
1020   {
1021     std::unordered_set<std::string> attribNames;
1022     for (const auto& attrib : linkInfo->active.activeAttribs) {
1023       attribNames.insert(attrib.name);
1024     }
1025     for (const auto& uniform : linkInfo->active.activeUniforms) {
1026       auto name = uniform.name;
1027       const auto maybe = webgl::ParseIndexed(name);
1028       if (maybe) {
1029         name = maybe->name;
1030       }
1031       if (attribNames.count(name)) {
1032         *out_linkLog = nsPrintfCString(
1033                            "Attrib name conflicts with uniform name:"
1034                            " %s",
1035                            name.c_str())
1036                            .BeginReading();
1037         return false;
1038       }
1039     }
1040   }
1041   {
1042     std::unordered_map<uint32_t, const std::string&> nameByLoc;
1043     for (const auto& attrib : linkInfo->active.activeAttribs) {
1044       if (attrib.location == -1) continue;
1045 
1046       const auto& elemType = attrib.elemType;
1047       const auto numUsedLocs = NumUsedLocationsByElemType(elemType);
1048       for (uint32_t i = 0; i < numUsedLocs; i++) {
1049         const uint32_t usedLoc = attrib.location + i;
1050 
1051         const auto res = nameByLoc.insert({usedLoc, attrib.name});
1052         const bool& didInsert = res.second;
1053         if (!didInsert) {
1054           const auto& aliasingName = attrib.name;
1055           const auto& itrExisting = res.first;
1056           const auto& existingName = itrExisting->second;
1057           *out_linkLog = nsPrintfCString(
1058                              "Attrib \"%s\" aliases locations used by"
1059                              " attrib \"%s\".",
1060                              aliasingName.c_str(), existingName.c_str())
1061                              .BeginReading();
1062           return false;
1063         }
1064       }
1065     }
1066   }
1067 
1068   // Forbid too many components for specified buffer mode
1069   const auto& activeTfVaryings = linkInfo->active.activeTfVaryings;
1070   MOZ_ASSERT(mNextLink_TransformFeedbackVaryings.size() ==
1071              activeTfVaryings.size());
1072   if (!activeTfVaryings.empty()) {
1073     GLuint maxComponentsPerIndex = 0;
1074     switch (linkInfo->transformFeedbackBufferMode) {
1075       case LOCAL_GL_INTERLEAVED_ATTRIBS:
1076         gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS,
1077                          &maxComponentsPerIndex);
1078         break;
1079 
1080       case LOCAL_GL_SEPARATE_ATTRIBS:
1081         gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS,
1082                          &maxComponentsPerIndex);
1083         break;
1084 
1085       default:
1086         MOZ_CRASH("`bufferMode`");
1087     }
1088 
1089     std::vector<size_t> componentsPerVert;
1090     for (const auto& cur : activeTfVaryings) {
1091       if (componentsPerVert.empty() ||
1092           linkInfo->transformFeedbackBufferMode == LOCAL_GL_SEPARATE_ATTRIBS) {
1093         componentsPerVert.push_back(0);
1094       }
1095 
1096       size_t varyingComponents = ElemTypeComponents(cur.elemType);
1097       MOZ_ASSERT(varyingComponents);
1098       varyingComponents *= cur.elemCount;
1099 
1100       auto& totalComponentsForIndex = *(componentsPerVert.rbegin());
1101       totalComponentsForIndex += varyingComponents;
1102 
1103       if (totalComponentsForIndex > maxComponentsPerIndex) {
1104         *out_linkLog = nsPrintfCString(
1105                            "Transform feedback varying \"%s\""
1106                            " pushed `componentsForIndex` over the"
1107                            " limit of %u.",
1108                            cur.name.c_str(), maxComponentsPerIndex)
1109                            .BeginReading();
1110         return false;
1111       }
1112     }
1113 
1114     linkInfo->componentsPerTFVert = std::move(componentsPerVert);
1115   }
1116 
1117   return true;
1118 }
1119 
UseProgram() const1120 bool WebGLProgram::UseProgram() const {
1121   if (!mMostRecentLinkInfo) {
1122     mContext->ErrorInvalidOperation(
1123         "Program has not been successfully linked.");
1124     return false;
1125   }
1126 
1127   if (mContext->mBoundTransformFeedback &&
1128       mContext->mBoundTransformFeedback->mIsActive &&
1129       !mContext->mBoundTransformFeedback->mIsPaused) {
1130     mContext->ErrorInvalidOperation(
1131         "Transform feedback active and not paused.");
1132     return false;
1133   }
1134 
1135   mContext->gl->fUseProgram(mGLName);
1136   return true;
1137 }
1138 
ValidateProgram() const1139 bool WebGLProgram::ValidateProgram() const {
1140   gl::GLContext* gl = mContext->gl;
1141 
1142 #ifdef XP_MACOSX
1143   // See bug 593867 for NVIDIA and bug 657201 for ATI. The latter is confirmed
1144   // with Mac OS 10.6.7.
1145   if (gl->WorkAroundDriverBugs()) {
1146     mContext->GenerateWarning(
1147         "Implemented as a no-op on"
1148         " Mac to work around crashes.");
1149     return true;
1150   }
1151 #endif
1152 
1153   gl->fValidateProgram(mGLName);
1154   GLint ok = 0;
1155   gl->fGetProgramiv(mGLName, LOCAL_GL_VALIDATE_STATUS, &ok);
1156   return bool(ok);
1157 }
1158 
1159 ////////////////////////////////////////////////////////////////////////////////
1160 
LinkAndUpdate()1161 void WebGLProgram::LinkAndUpdate() {
1162   mMostRecentLinkInfo = nullptr;
1163 
1164   gl::GLContext* gl = mContext->gl;
1165   gl->fLinkProgram(mGLName);
1166 
1167   // Grab the program log.
1168   {
1169     GLuint logLenWithNull = 0;
1170     gl->fGetProgramiv(mGLName, LOCAL_GL_INFO_LOG_LENGTH,
1171                       (GLint*)&logLenWithNull);
1172     if (logLenWithNull > 1) {
1173       std::vector<char> buffer(logLenWithNull);
1174       gl->fGetProgramInfoLog(mGLName, buffer.size(), nullptr, buffer.data());
1175       mLinkLog = buffer.data();
1176     } else {
1177       mLinkLog.clear();
1178     }
1179   }
1180 
1181   GLint ok = 0;
1182   gl->fGetProgramiv(mGLName, LOCAL_GL_LINK_STATUS, &ok);
1183   if (!ok) return;
1184 
1185   mMostRecentLinkInfo =
1186       QueryProgramInfo(this, gl);  // Fallible after context loss.
1187 }
1188 
TransformFeedbackVaryings(const std::vector<std::string> & varyings,GLenum bufferMode)1189 void WebGLProgram::TransformFeedbackVaryings(
1190     const std::vector<std::string>& varyings, GLenum bufferMode) {
1191   const auto& gl = mContext->gl;
1192 
1193   switch (bufferMode) {
1194     case LOCAL_GL_INTERLEAVED_ATTRIBS:
1195       break;
1196 
1197     case LOCAL_GL_SEPARATE_ATTRIBS: {
1198       GLuint maxAttribs = 0;
1199       gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS,
1200                        &maxAttribs);
1201       if (varyings.size() > maxAttribs) {
1202         mContext->ErrorInvalidValue("Length of `varyings` exceeds %s.",
1203                                     "TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS");
1204         return;
1205       }
1206     } break;
1207 
1208     default:
1209       mContext->ErrorInvalidEnumInfo("bufferMode", bufferMode);
1210       return;
1211   }
1212 
1213   ////
1214 
1215   mNextLink_TransformFeedbackVaryings = varyings;
1216   mNextLink_TransformFeedbackBufferMode = bufferMode;
1217 }
1218 
1219 }  // namespace mozilla
1220