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