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