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