1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 "WebGLActiveInfo.h"
15 #include "WebGLContext.h"
16 #include "WebGLShader.h"
17 #include "WebGLTransformFeedback.h"
18 #include "WebGLUniformLocation.h"
19 #include "WebGLValidateStrings.h"
20 #include "WebGLObjectModel.h"
21
22 namespace mozilla {
23
24 /* If `name`: "foo[3]"
25 * Then returns true, with
26 * `out_baseName`: "foo"
27 * `out_isArray`: true
28 * `out_index`: 3
29 *
30 * If `name`: "foo"
31 * Then returns true, with
32 * `out_baseName`: "foo"
33 * `out_isArray`: false
34 * `out_index`: 0
35 */
36 static bool
ParseName(const nsCString & name,nsCString * const out_baseName,bool * const out_isArray,size_t * const out_arrayIndex)37 ParseName(const nsCString& name, nsCString* const out_baseName,
38 bool* const out_isArray, size_t* const out_arrayIndex)
39 {
40 int32_t indexEnd = name.RFind("]");
41 if (indexEnd == -1 ||
42 (uint32_t)indexEnd != name.Length() - 1)
43 {
44 *out_baseName = name;
45 *out_isArray = false;
46 *out_arrayIndex = 0;
47 return true;
48 }
49
50 int32_t indexOpenBracket = name.RFind("[");
51 if (indexOpenBracket == -1)
52 return false;
53
54 uint32_t indexStart = indexOpenBracket + 1;
55 uint32_t indexLen = indexEnd - indexStart;
56 if (indexLen == 0)
57 return false;
58
59 const nsAutoCString indexStr(Substring(name, indexStart, indexLen));
60
61 nsresult errorcode;
62 int32_t indexNum = indexStr.ToInteger(&errorcode);
63 if (NS_FAILED(errorcode))
64 return false;
65
66 if (indexNum < 0)
67 return false;
68
69 *out_baseName = StringHead(name, indexOpenBracket);
70 *out_isArray = true;
71 *out_arrayIndex = indexNum;
72 return true;
73 }
74
75 static void
AssembleName(const nsCString & baseName,bool isArray,size_t arrayIndex,nsCString * const out_name)76 AssembleName(const nsCString& baseName, bool isArray, size_t arrayIndex,
77 nsCString* const out_name)
78 {
79 *out_name = baseName;
80 if (isArray) {
81 out_name->Append('[');
82 out_name->AppendInt(uint64_t(arrayIndex));
83 out_name->Append(']');
84 }
85 }
86
87 ////
88
89 static GLenum
AttribBaseType(GLenum attribType)90 AttribBaseType(GLenum attribType)
91 {
92 switch (attribType) {
93 case LOCAL_GL_FLOAT:
94 case LOCAL_GL_FLOAT_VEC2:
95 case LOCAL_GL_FLOAT_VEC3:
96 case LOCAL_GL_FLOAT_VEC4:
97
98 case LOCAL_GL_FLOAT_MAT2:
99 case LOCAL_GL_FLOAT_MAT2x3:
100 case LOCAL_GL_FLOAT_MAT2x4:
101
102 case LOCAL_GL_FLOAT_MAT3x2:
103 case LOCAL_GL_FLOAT_MAT3:
104 case LOCAL_GL_FLOAT_MAT3x4:
105
106 case LOCAL_GL_FLOAT_MAT4x2:
107 case LOCAL_GL_FLOAT_MAT4x3:
108 case LOCAL_GL_FLOAT_MAT4:
109 return LOCAL_GL_FLOAT;
110
111 case LOCAL_GL_INT:
112 case LOCAL_GL_INT_VEC2:
113 case LOCAL_GL_INT_VEC3:
114 case LOCAL_GL_INT_VEC4:
115 return LOCAL_GL_INT;
116
117 case LOCAL_GL_UNSIGNED_INT:
118 case LOCAL_GL_UNSIGNED_INT_VEC2:
119 case LOCAL_GL_UNSIGNED_INT_VEC3:
120 case LOCAL_GL_UNSIGNED_INT_VEC4:
121 return LOCAL_GL_UNSIGNED_INT;
122
123 default:
124 MOZ_ASSERT(false, "unexpected attrib elemType");
125 return 0;
126 }
127 }
128
129 ////
130
131 /*static*/ const webgl::UniformInfo::TexListT*
GetTexList(WebGLActiveInfo * activeInfo)132 webgl::UniformInfo::GetTexList(WebGLActiveInfo* activeInfo)
133 {
134 const auto& webgl = activeInfo->mWebGL;
135
136 switch (activeInfo->mElemType) {
137 case LOCAL_GL_SAMPLER_2D:
138 case LOCAL_GL_SAMPLER_2D_SHADOW:
139 case LOCAL_GL_INT_SAMPLER_2D:
140 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
141 return &webgl->mBound2DTextures;
142
143 case LOCAL_GL_SAMPLER_CUBE:
144 case LOCAL_GL_SAMPLER_CUBE_SHADOW:
145 case LOCAL_GL_INT_SAMPLER_CUBE:
146 case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
147 return &webgl->mBoundCubeMapTextures;
148
149 case LOCAL_GL_SAMPLER_3D:
150 case LOCAL_GL_INT_SAMPLER_3D:
151 case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
152 return &webgl->mBound3DTextures;
153
154 case LOCAL_GL_SAMPLER_2D_ARRAY:
155 case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
156 case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
157 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
158 return &webgl->mBound2DArrayTextures;
159
160 default:
161 return nullptr;
162 }
163 }
164
UniformInfo(WebGLActiveInfo * activeInfo)165 webgl::UniformInfo::UniformInfo(WebGLActiveInfo* activeInfo)
166 : mActiveInfo(activeInfo)
167 , mSamplerTexList(GetTexList(activeInfo))
168 {
169 if (mSamplerTexList) {
170 mSamplerValues.assign(mActiveInfo->mElemCount, 0);
171 }
172 }
173
174 //////////
175
176 //#define DUMP_SHADERVAR_MAPPINGS
177
178 static already_AddRefed<const webgl::LinkedProgramInfo>
QueryProgramInfo(WebGLProgram * prog,gl::GLContext * gl)179 QueryProgramInfo(WebGLProgram* prog, gl::GLContext* gl)
180 {
181 WebGLContext* const webgl = prog->mContext;
182
183 RefPtr<webgl::LinkedProgramInfo> info(new webgl::LinkedProgramInfo(prog));
184
185 GLuint maxAttribLenWithNull = 0;
186 gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_ATTRIBUTE_MAX_LENGTH,
187 (GLint*)&maxAttribLenWithNull);
188 if (maxAttribLenWithNull < 1)
189 maxAttribLenWithNull = 1;
190
191 GLuint maxUniformLenWithNull = 0;
192 gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_UNIFORM_MAX_LENGTH,
193 (GLint*)&maxUniformLenWithNull);
194 if (maxUniformLenWithNull < 1)
195 maxUniformLenWithNull = 1;
196
197 GLuint maxUniformBlockLenWithNull = 0;
198 if (gl->IsSupported(gl::GLFeature::uniform_buffer_object)) {
199 gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH,
200 (GLint*)&maxUniformBlockLenWithNull);
201 if (maxUniformBlockLenWithNull < 1)
202 maxUniformBlockLenWithNull = 1;
203 }
204
205 GLuint maxTransformFeedbackVaryingLenWithNull = 0;
206 if (gl->IsSupported(gl::GLFeature::transform_feedback2)) {
207 gl->fGetProgramiv(prog->mGLName, LOCAL_GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH,
208 (GLint*)&maxTransformFeedbackVaryingLenWithNull);
209 if (maxTransformFeedbackVaryingLenWithNull < 1)
210 maxTransformFeedbackVaryingLenWithNull = 1;
211 }
212
213 // Attribs (can't be arrays)
214
215 GLuint numActiveAttribs = 0;
216 gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_ATTRIBUTES,
217 (GLint*)&numActiveAttribs);
218
219 for (GLuint i = 0; i < numActiveAttribs; i++) {
220 nsAutoCString mappedName;
221 mappedName.SetLength(maxAttribLenWithNull - 1);
222
223 GLsizei lengthWithoutNull = 0;
224 GLint elemCount = 0; // `size`
225 GLenum elemType = 0; // `type`
226 gl->fGetActiveAttrib(prog->mGLName, i, mappedName.Length()+1, &lengthWithoutNull,
227 &elemCount, &elemType, mappedName.BeginWriting());
228 GLenum error = gl->fGetError();
229 if (error != LOCAL_GL_NO_ERROR) {
230 gfxCriticalNote << "Failed to do glGetActiveAttrib: " << error;
231 }
232
233 mappedName.SetLength(lengthWithoutNull);
234
235 ////
236
237 nsCString userName;
238 if (!prog->FindAttribUserNameByMappedName(mappedName, &userName)) {
239 userName = mappedName;
240 }
241
242 ///////
243
244 GLint loc = gl->fGetAttribLocation(prog->mGLName,
245 mappedName.BeginReading());
246 if (gl->WorkAroundDriverBugs() &&
247 mappedName.EqualsIgnoreCase("gl_", 3))
248 {
249 // Bug 1328559: Appears problematic on ANGLE and OSX, but not Linux or Win+GL.
250 loc = -1;
251 }
252 #ifdef DUMP_SHADERVAR_MAPPINGS
253 printf_stderr("[attrib %u/%u] @%i %s->%s\n", i, numActiveAttribs, loc,
254 userName.BeginReading(), mappedName.BeginReading());
255 #endif
256 MOZ_ASSERT_IF(mappedName.EqualsIgnoreCase("gl_", 3), loc == -1);
257
258 ///////
259
260 const bool isArray = false;
261 const RefPtr<WebGLActiveInfo> activeInfo = new WebGLActiveInfo(webgl, elemCount,
262 elemType, isArray,
263 userName,
264 mappedName);
265 const GLenum baseType = AttribBaseType(elemType);
266 const webgl::AttribInfo attrib = {activeInfo, loc, baseType};
267 info->attribs.push_back(attrib);
268 }
269
270 // Uniforms (can be basically anything)
271
272 const bool needsCheckForArrays = gl->WorkAroundDriverBugs();
273
274 GLuint numActiveUniforms = 0;
275 gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_UNIFORMS,
276 (GLint*)&numActiveUniforms);
277
278 for (GLuint i = 0; i < numActiveUniforms; i++) {
279 nsAutoCString mappedName;
280 mappedName.SetLength(maxUniformLenWithNull - 1);
281
282 GLsizei lengthWithoutNull = 0;
283 GLint elemCount = 0; // `size`
284 GLenum elemType = 0; // `type`
285 gl->fGetActiveUniform(prog->mGLName, i, mappedName.Length()+1, &lengthWithoutNull,
286 &elemCount, &elemType, mappedName.BeginWriting());
287
288 mappedName.SetLength(lengthWithoutNull);
289
290 ///////
291
292 nsAutoCString baseMappedName;
293 bool isArray;
294 size_t arrayIndex;
295 if (!ParseName(mappedName, &baseMappedName, &isArray, &arrayIndex))
296 MOZ_CRASH("GFX: Failed to parse `mappedName` received from driver.");
297
298 // Note that for good drivers, `isArray` should already be correct.
299 // However, if FindUniform succeeds, it will be validator-guaranteed correct.
300
301 ///////
302
303 nsAutoCString baseUserName;
304 if (!prog->FindUniformByMappedName(baseMappedName, &baseUserName, &isArray)) {
305 // Validator likely missing.
306 baseUserName = baseMappedName;
307
308 if (needsCheckForArrays && !isArray) {
309 // By GLES 3, GetUniformLocation("foo[0]") should return -1 if `foo` is
310 // not an array. Our current linux Try slaves return the location of `foo`
311 // anyways, though.
312 std::string mappedNameStr = baseMappedName.BeginReading();
313 mappedNameStr += "[0]";
314
315 GLint loc = gl->fGetUniformLocation(prog->mGLName, mappedNameStr.c_str());
316 if (loc != -1)
317 isArray = true;
318 }
319 }
320
321 ///////
322
323 #ifdef DUMP_SHADERVAR_MAPPINGS
324 printf_stderr("[uniform %u/%u] %s->%s\n", i, numActiveUniforms,
325 baseUserName.BeginReading(), mappedName.BeginReading());
326 #endif
327
328 ///////
329
330 const RefPtr<WebGLActiveInfo> activeInfo = new WebGLActiveInfo(webgl, elemCount,
331 elemType, isArray,
332 baseUserName,
333 baseMappedName);
334
335 auto* uniform = new webgl::UniformInfo(activeInfo);
336 info->uniforms.push_back(uniform);
337
338 if (uniform->mSamplerTexList) {
339 info->uniformSamplers.push_back(uniform);
340 }
341 }
342
343 // Uniform Blocks (can be arrays, but can't contain sampler types)
344
345 if (gl->IsSupported(gl::GLFeature::uniform_buffer_object)) {
346 GLuint numActiveUniformBlocks = 0;
347 gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_UNIFORM_BLOCKS,
348 (GLint*)&numActiveUniformBlocks);
349
350 for (GLuint i = 0; i < numActiveUniformBlocks; i++) {
351 nsAutoCString mappedName;
352 mappedName.SetLength(maxUniformBlockLenWithNull - 1);
353
354 GLint lengthWithoutNull;
355 gl->fGetActiveUniformBlockiv(prog->mGLName, i, LOCAL_GL_UNIFORM_BLOCK_NAME_LENGTH, &lengthWithoutNull);
356 gl->fGetActiveUniformBlockName(prog->mGLName, i, maxUniformBlockLenWithNull, &lengthWithoutNull, mappedName.BeginWriting());
357 mappedName.SetLength(lengthWithoutNull);
358
359 ////
360
361 nsCString userName;
362 if (!prog->UnmapUniformBlockName(mappedName, &userName))
363 continue;
364
365 #ifdef DUMP_SHADERVAR_MAPPINGS
366 printf_stderr("[uniform block %u/%u] %s->%s\n", i, numActiveUniformBlocks,
367 userName.BeginReading(), mappedName.BeginReading());
368 #endif
369
370 ////
371
372 GLuint dataSize = 0;
373 gl->fGetActiveUniformBlockiv(prog->mGLName, i,
374 LOCAL_GL_UNIFORM_BLOCK_DATA_SIZE,
375 (GLint*)&dataSize);
376
377
378 auto* block = new webgl::UniformBlockInfo(webgl, userName, mappedName,
379 dataSize);
380 info->uniformBlocks.push_back(block);
381 }
382 }
383
384 // Transform feedback varyings (can be arrays)
385
386 if (gl->IsSupported(gl::GLFeature::transform_feedback2)) {
387 GLuint numTransformFeedbackVaryings = 0;
388 gl->fGetProgramiv(prog->mGLName, LOCAL_GL_TRANSFORM_FEEDBACK_VARYINGS,
389 (GLint*)&numTransformFeedbackVaryings);
390
391 for (GLuint i = 0; i < numTransformFeedbackVaryings; i++) {
392 nsAutoCString mappedName;
393 mappedName.SetLength(maxTransformFeedbackVaryingLenWithNull - 1);
394
395 GLint lengthWithoutNull;
396 GLsizei elemCount;
397 GLenum elemType;
398 gl->fGetTransformFeedbackVarying(prog->mGLName, i,
399 maxTransformFeedbackVaryingLenWithNull,
400 &lengthWithoutNull, &elemCount, &elemType,
401 mappedName.BeginWriting());
402 mappedName.SetLength(lengthWithoutNull);
403
404 ////
405
406 nsAutoCString baseMappedName;
407 bool isArray;
408 size_t arrayIndex;
409 if (!ParseName(mappedName, &baseMappedName, &isArray, &arrayIndex))
410 MOZ_CRASH("GFX: Failed to parse `mappedName` received from driver.");
411
412 nsAutoCString baseUserName;
413 if (!prog->FindVaryingByMappedName(mappedName, &baseUserName, &isArray)) {
414 baseUserName = baseMappedName;
415 }
416
417 ////
418
419 #ifdef DUMP_SHADERVAR_MAPPINGS
420 printf_stderr("[transform feedback varying %u/%u] %s->%s\n", i,
421 numTransformFeedbackVaryings, baseUserName.BeginReading(),
422 mappedName.BeginReading());
423 #endif
424
425 const RefPtr<WebGLActiveInfo> activeInfo = new WebGLActiveInfo(webgl,
426 elemCount,
427 elemType,
428 isArray,
429 baseUserName,
430 mappedName);
431 info->transformFeedbackVaryings.push_back(activeInfo);
432 }
433 }
434
435 // Frag outputs
436
437 prog->EnumerateFragOutputs(info->fragDataMap);
438
439 return info.forget();
440 }
441
442 ////////////////////////////////////////////////////////////////////////////////
443
LinkedProgramInfo(WebGLProgram * prog)444 webgl::LinkedProgramInfo::LinkedProgramInfo(WebGLProgram* prog)
445 : prog(prog)
446 , transformFeedbackBufferMode(prog->mNextLink_TransformFeedbackBufferMode)
447 { }
448
~LinkedProgramInfo()449 webgl::LinkedProgramInfo::~LinkedProgramInfo()
450 {
451 for (auto& cur : uniforms) {
452 delete cur;
453 }
454 for (auto& cur : uniformBlocks) {
455 delete cur;
456 }
457 }
458
459 ////////////////////////////////////////////////////////////////////////////////
460 // WebGLProgram
461
462 static GLuint
CreateProgram(gl::GLContext * gl)463 CreateProgram(gl::GLContext* gl)
464 {
465 gl->MakeCurrent();
466 return gl->fCreateProgram();
467 }
468
WebGLProgram(WebGLContext * webgl)469 WebGLProgram::WebGLProgram(WebGLContext* webgl)
470 : WebGLRefCountedObject(webgl)
471 , mGLName(CreateProgram(webgl->GL()))
472 , mNumActiveTFOs(0)
473 , mNextLink_TransformFeedbackBufferMode(LOCAL_GL_INTERLEAVED_ATTRIBS)
474 {
475 mContext->mPrograms.insertBack(this);
476 }
477
~WebGLProgram()478 WebGLProgram::~WebGLProgram()
479 {
480 DeleteOnce();
481 }
482
483 void
Delete()484 WebGLProgram::Delete()
485 {
486 gl::GLContext* gl = mContext->GL();
487
488 gl->MakeCurrent();
489 gl->fDeleteProgram(mGLName);
490
491 mVertShader = nullptr;
492 mFragShader = nullptr;
493
494 mMostRecentLinkInfo = nullptr;
495
496 LinkedListElement<WebGLProgram>::removeFrom(mContext->mPrograms);
497 }
498
499 ////////////////////////////////////////////////////////////////////////////////
500 // GL funcs
501
502 void
AttachShader(WebGLShader * shader)503 WebGLProgram::AttachShader(WebGLShader* shader)
504 {
505 WebGLRefPtr<WebGLShader>* shaderSlot;
506 switch (shader->mType) {
507 case LOCAL_GL_VERTEX_SHADER:
508 shaderSlot = &mVertShader;
509 break;
510 case LOCAL_GL_FRAGMENT_SHADER:
511 shaderSlot = &mFragShader;
512 break;
513 default:
514 mContext->ErrorInvalidOperation("attachShader: Bad type for shader.");
515 return;
516 }
517
518 if (*shaderSlot) {
519 if (shader == *shaderSlot) {
520 mContext->ErrorInvalidOperation("attachShader: `shader` is already attached.");
521 } else {
522 mContext->ErrorInvalidOperation("attachShader: Only one of each type of"
523 " shader may be attached to a program.");
524 }
525 return;
526 }
527
528 *shaderSlot = shader;
529
530 mContext->MakeContextCurrent();
531 mContext->gl->fAttachShader(mGLName, shader->mGLName);
532 }
533
534 void
BindAttribLocation(GLuint loc,const nsAString & name)535 WebGLProgram::BindAttribLocation(GLuint loc, const nsAString& name)
536 {
537 if (!ValidateGLSLVariableName(name, mContext, "bindAttribLocation"))
538 return;
539
540 if (loc >= mContext->MaxVertexAttribs()) {
541 mContext->ErrorInvalidValue("bindAttribLocation: `location` must be less than"
542 " MAX_VERTEX_ATTRIBS.");
543 return;
544 }
545
546 if (StringBeginsWith(name, NS_LITERAL_STRING("gl_"))) {
547 mContext->ErrorInvalidOperation("bindAttribLocation: Can't set the location of a"
548 " name that starts with 'gl_'.");
549 return;
550 }
551
552 NS_LossyConvertUTF16toASCII asciiName(name);
553
554 auto res = mNextLink_BoundAttribLocs.insert({asciiName, loc});
555
556 const bool wasInserted = res.second;
557 if (!wasInserted) {
558 auto itr = res.first;
559 itr->second = loc;
560 }
561 }
562
563 void
DetachShader(const WebGLShader * shader)564 WebGLProgram::DetachShader(const WebGLShader* shader)
565 {
566 MOZ_ASSERT(shader);
567
568 WebGLRefPtr<WebGLShader>* shaderSlot;
569 switch (shader->mType) {
570 case LOCAL_GL_VERTEX_SHADER:
571 shaderSlot = &mVertShader;
572 break;
573 case LOCAL_GL_FRAGMENT_SHADER:
574 shaderSlot = &mFragShader;
575 break;
576 default:
577 mContext->ErrorInvalidOperation("attachShader: Bad type for shader.");
578 return;
579 }
580
581 if (*shaderSlot != shader) {
582 mContext->ErrorInvalidOperation("detachShader: `shader` is not attached.");
583 return;
584 }
585
586 *shaderSlot = nullptr;
587
588 mContext->MakeContextCurrent();
589 mContext->gl->fDetachShader(mGLName, shader->mGLName);
590 }
591
592 already_AddRefed<WebGLActiveInfo>
GetActiveAttrib(GLuint index) const593 WebGLProgram::GetActiveAttrib(GLuint index) const
594 {
595 if (!mMostRecentLinkInfo) {
596 RefPtr<WebGLActiveInfo> ret = WebGLActiveInfo::CreateInvalid(mContext);
597 return ret.forget();
598 }
599
600 const auto& attribs = mMostRecentLinkInfo->attribs;
601
602 if (index >= attribs.size()) {
603 mContext->ErrorInvalidValue("`index` (%i) must be less than %s (%i).",
604 index, "ACTIVE_ATTRIBS", attribs.size());
605 return nullptr;
606 }
607
608 RefPtr<WebGLActiveInfo> ret = attribs[index].mActiveInfo;
609 return ret.forget();
610 }
611
612 already_AddRefed<WebGLActiveInfo>
GetActiveUniform(GLuint index) const613 WebGLProgram::GetActiveUniform(GLuint index) const
614 {
615 if (!mMostRecentLinkInfo) {
616 // According to the spec, this can return null.
617 RefPtr<WebGLActiveInfo> ret = WebGLActiveInfo::CreateInvalid(mContext);
618 return ret.forget();
619 }
620
621 const auto& uniforms = mMostRecentLinkInfo->uniforms;
622
623 if (index >= uniforms.size()) {
624 mContext->ErrorInvalidValue("`index` (%i) must be less than %s (%i).",
625 index, "ACTIVE_UNIFORMS", uniforms.size());
626 return nullptr;
627 }
628
629 RefPtr<WebGLActiveInfo> ret = uniforms[index]->mActiveInfo;
630 return ret.forget();
631 }
632
633 void
GetAttachedShaders(nsTArray<RefPtr<WebGLShader>> * const out) const634 WebGLProgram::GetAttachedShaders(nsTArray<RefPtr<WebGLShader>>* const out) const
635 {
636 out->TruncateLength(0);
637
638 if (mVertShader)
639 out->AppendElement(mVertShader);
640
641 if (mFragShader)
642 out->AppendElement(mFragShader);
643 }
644
645 GLint
GetAttribLocation(const nsAString & userName_wide) const646 WebGLProgram::GetAttribLocation(const nsAString& userName_wide) const
647 {
648 if (!ValidateGLSLVariableName(userName_wide, mContext, "getAttribLocation"))
649 return -1;
650
651 if (!IsLinked()) {
652 mContext->ErrorInvalidOperation("getAttribLocation: `program` must be linked.");
653 return -1;
654 }
655
656 const NS_LossyConvertUTF16toASCII userName(userName_wide);
657
658 const webgl::AttribInfo* info;
659 if (!LinkInfo()->FindAttrib(userName, &info))
660 return -1;
661
662 return GLint(info->mLoc);
663 }
664
665 static GLint
GetFragDataByUserName(const WebGLProgram * prog,const nsCString & userName)666 GetFragDataByUserName(const WebGLProgram* prog,
667 const nsCString& userName)
668 {
669 nsCString mappedName;
670 if (!prog->LinkInfo()->MapFragDataName(userName, &mappedName))
671 return -1;
672
673 return prog->mContext->gl->fGetFragDataLocation(prog->mGLName, mappedName.BeginReading());
674 }
675
676 GLint
GetFragDataLocation(const nsAString & userName_wide) const677 WebGLProgram::GetFragDataLocation(const nsAString& userName_wide) const
678 {
679 if (!ValidateGLSLVariableName(userName_wide, mContext, "getFragDataLocation"))
680 return -1;
681
682 if (!IsLinked()) {
683 mContext->ErrorInvalidOperation("getFragDataLocation: `program` must be linked.");
684 return -1;
685 }
686
687
688 const auto& gl = mContext->gl;
689 gl->MakeCurrent();
690
691 const NS_LossyConvertUTF16toASCII userName(userName_wide);
692 #ifdef XP_MACOSX
693 if (gl->WorkAroundDriverBugs()) {
694 // OSX doesn't return locs for indexed names, just the base names.
695 // Indicated by failure in: conformance2/programs/gl-get-frag-data-location.html
696 bool isArray;
697 size_t arrayIndex;
698 nsCString baseUserName;
699 if (!ParseName(userName, &baseUserName, &isArray, &arrayIndex))
700 return -1;
701
702 if (arrayIndex >= mContext->mImplMaxDrawBuffers)
703 return -1;
704
705 const auto baseLoc = GetFragDataByUserName(this, baseUserName);
706 const auto loc = baseLoc + GLint(arrayIndex);
707 return loc;
708 }
709 #endif
710 return GetFragDataByUserName(this, userName);
711 }
712
713 void
GetProgramInfoLog(nsAString * const out) const714 WebGLProgram::GetProgramInfoLog(nsAString* const out) const
715 {
716 CopyASCIItoUTF16(mLinkLog, *out);
717 }
718
719 static GLint
GetProgramiv(gl::GLContext * gl,GLuint program,GLenum pname)720 GetProgramiv(gl::GLContext* gl, GLuint program, GLenum pname)
721 {
722 GLint ret = 0;
723 gl->fGetProgramiv(program, pname, &ret);
724 return ret;
725 }
726
727 JS::Value
GetProgramParameter(GLenum pname) const728 WebGLProgram::GetProgramParameter(GLenum pname) const
729 {
730 gl::GLContext* gl = mContext->gl;
731 gl->MakeCurrent();
732
733 if (mContext->IsWebGL2()) {
734 switch (pname) {
735 case LOCAL_GL_ACTIVE_UNIFORM_BLOCKS:
736 if (!IsLinked())
737 return JS::NumberValue(0);
738 return JS::NumberValue(LinkInfo()->uniformBlocks.size());
739
740 case LOCAL_GL_TRANSFORM_FEEDBACK_VARYINGS:
741 if (!IsLinked())
742 return JS::NumberValue(0);
743 return JS::NumberValue(LinkInfo()->transformFeedbackVaryings.size());
744
745 case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_MODE:
746 if (!IsLinked())
747 return JS::NumberValue(LOCAL_GL_INTERLEAVED_ATTRIBS);
748 return JS::NumberValue(LinkInfo()->transformFeedbackBufferMode);
749 }
750 }
751
752 switch (pname) {
753 case LOCAL_GL_ATTACHED_SHADERS:
754 return JS::NumberValue( int(bool(mVertShader.get())) + int(bool(mFragShader)) );
755
756 case LOCAL_GL_ACTIVE_UNIFORMS:
757 if (!IsLinked())
758 return JS::NumberValue(0);
759 return JS::NumberValue(LinkInfo()->uniforms.size());
760
761 case LOCAL_GL_ACTIVE_ATTRIBUTES:
762 if (!IsLinked())
763 return JS::NumberValue(0);
764 return JS::NumberValue(LinkInfo()->attribs.size());
765
766 case LOCAL_GL_DELETE_STATUS:
767 return JS::BooleanValue(IsDeleteRequested());
768
769 case LOCAL_GL_LINK_STATUS:
770 return JS::BooleanValue(IsLinked());
771
772 case LOCAL_GL_VALIDATE_STATUS:
773 #ifdef XP_MACOSX
774 // See comment in ValidateProgram.
775 if (gl->WorkAroundDriverBugs())
776 return JS::BooleanValue(true);
777 #endif
778 // Todo: Implement this in our code.
779 return JS::BooleanValue(bool(GetProgramiv(gl, mGLName, pname)));
780
781 default:
782 mContext->ErrorInvalidEnumInfo("getProgramParameter: `pname`",
783 pname);
784 return JS::NullValue();
785 }
786 }
787
788 GLuint
GetUniformBlockIndex(const nsAString & userName_wide) const789 WebGLProgram::GetUniformBlockIndex(const nsAString& userName_wide) const
790 {
791 if (!ValidateGLSLVariableName(userName_wide, mContext, "getUniformBlockIndex"))
792 return LOCAL_GL_INVALID_INDEX;
793
794 if (!IsLinked()) {
795 mContext->ErrorInvalidOperation("getUniformBlockIndex: `program` must be linked.");
796 return LOCAL_GL_INVALID_INDEX;
797 }
798
799 const NS_LossyConvertUTF16toASCII userName(userName_wide);
800
801 const webgl::UniformBlockInfo* info = nullptr;
802 for (const auto& cur : LinkInfo()->uniformBlocks) {
803 if (cur->mUserName == userName) {
804 info = cur;
805 break;
806 }
807 }
808 if (!info)
809 return LOCAL_GL_INVALID_INDEX;
810
811 const auto& mappedName = info->mMappedName;
812
813 gl::GLContext* gl = mContext->GL();
814 gl->MakeCurrent();
815 return gl->fGetUniformBlockIndex(mGLName, mappedName.BeginReading());
816 }
817
818 void
GetActiveUniformBlockName(GLuint uniformBlockIndex,nsAString & retval) const819 WebGLProgram::GetActiveUniformBlockName(GLuint uniformBlockIndex, nsAString& retval) const
820 {
821 if (!IsLinked()) {
822 mContext->ErrorInvalidOperation("getActiveUniformBlockName: `program` must be linked.");
823 return;
824 }
825
826 const webgl::LinkedProgramInfo* linkInfo = LinkInfo();
827 GLuint uniformBlockCount = (GLuint) linkInfo->uniformBlocks.size();
828 if (uniformBlockIndex >= uniformBlockCount) {
829 mContext->ErrorInvalidValue("getActiveUniformBlockName: index %u invalid.", uniformBlockIndex);
830 return;
831 }
832
833 const auto& blockInfo = linkInfo->uniformBlocks[uniformBlockIndex];
834 retval.Assign(NS_ConvertASCIItoUTF16(blockInfo->mUserName));
835 }
836
837 JS::Value
GetActiveUniformBlockParam(GLuint uniformBlockIndex,GLenum pname) const838 WebGLProgram::GetActiveUniformBlockParam(GLuint uniformBlockIndex, GLenum pname) const
839 {
840 if (!IsLinked()) {
841 mContext->ErrorInvalidOperation("getActiveUniformBlockParameter: `program` must be linked.");
842 return JS::NullValue();
843 }
844
845 const webgl::LinkedProgramInfo* linkInfo = LinkInfo();
846 GLuint uniformBlockCount = (GLuint)linkInfo->uniformBlocks.size();
847 if (uniformBlockIndex >= uniformBlockCount) {
848 mContext->ErrorInvalidValue("getActiveUniformBlockParameter: index %u invalid.", uniformBlockIndex);
849 return JS::NullValue();
850 }
851
852 gl::GLContext* gl = mContext->GL();
853 GLint param = 0;
854
855 switch (pname) {
856 case LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER:
857 case LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER:
858 gl->fGetActiveUniformBlockiv(mGLName, uniformBlockIndex, pname, ¶m);
859 return JS::BooleanValue(bool(param));
860
861 case LOCAL_GL_UNIFORM_BLOCK_BINDING:
862 case LOCAL_GL_UNIFORM_BLOCK_DATA_SIZE:
863 case LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS:
864 gl->fGetActiveUniformBlockiv(mGLName, uniformBlockIndex, pname, ¶m);
865 return JS::NumberValue(param);
866
867 default:
868 MOZ_CRASH("bad `pname`.");
869 }
870 }
871
872 JS::Value
GetActiveUniformBlockActiveUniforms(JSContext * cx,GLuint uniformBlockIndex,ErrorResult * const out_error) const873 WebGLProgram::GetActiveUniformBlockActiveUniforms(JSContext* cx, GLuint uniformBlockIndex,
874 ErrorResult* const out_error) const
875 {
876 const char funcName[] = "getActiveUniformBlockParameter";
877 if (!IsLinked()) {
878 mContext->ErrorInvalidOperation("%s: `program` must be linked.", funcName);
879 return JS::NullValue();
880 }
881
882 const webgl::LinkedProgramInfo* linkInfo = LinkInfo();
883 GLuint uniformBlockCount = (GLuint)linkInfo->uniformBlocks.size();
884 if (uniformBlockIndex >= uniformBlockCount) {
885 mContext->ErrorInvalidValue("%s: Index %u invalid.", funcName, uniformBlockIndex);
886 return JS::NullValue();
887 }
888
889 gl::GLContext* gl = mContext->GL();
890 GLint activeUniformCount = 0;
891 gl->fGetActiveUniformBlockiv(mGLName, uniformBlockIndex,
892 LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS,
893 &activeUniformCount);
894 JS::RootedObject obj(cx, dom::Uint32Array::Create(cx, mContext, activeUniformCount,
895 nullptr));
896 if (!obj) {
897 *out_error = NS_ERROR_OUT_OF_MEMORY;
898 return JS::NullValue();
899 }
900
901 dom::Uint32Array result;
902 DebugOnly<bool> inited = result.Init(obj);
903 MOZ_ASSERT(inited);
904 result.ComputeLengthAndData();
905 gl->fGetActiveUniformBlockiv(mGLName, uniformBlockIndex,
906 LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES,
907 (GLint*)result.Data());
908
909 return JS::ObjectValue(*obj);
910 }
911
912 already_AddRefed<WebGLUniformLocation>
GetUniformLocation(const nsAString & userName_wide) const913 WebGLProgram::GetUniformLocation(const nsAString& userName_wide) const
914 {
915 if (!ValidateGLSLVariableName(userName_wide, mContext, "getUniformLocation"))
916 return nullptr;
917
918 if (!IsLinked()) {
919 mContext->ErrorInvalidOperation("getUniformLocation: `program` must be linked.");
920 return nullptr;
921 }
922
923 const NS_LossyConvertUTF16toASCII userName(userName_wide);
924
925 // GLES 2.0.25, Section 2.10, p35
926 // If the the uniform location is an array, then the location of the first
927 // element of that array can be retrieved by either using the name of the
928 // uniform array, or the name of the uniform array appended with "[0]".
929 nsCString mappedName;
930 size_t arrayIndex;
931 webgl::UniformInfo* info;
932 if (!LinkInfo()->FindUniform(userName, &mappedName, &arrayIndex, &info))
933 return nullptr;
934
935 gl::GLContext* gl = mContext->GL();
936 gl->MakeCurrent();
937
938 GLint loc = gl->fGetUniformLocation(mGLName, mappedName.BeginReading());
939 if (loc == -1)
940 return nullptr;
941
942 RefPtr<WebGLUniformLocation> locObj = new WebGLUniformLocation(mContext, LinkInfo(),
943 info, loc, arrayIndex);
944 return locObj.forget();
945 }
946
947 void
GetUniformIndices(const dom::Sequence<nsString> & uniformNames,dom::Nullable<nsTArray<GLuint>> & retval) const948 WebGLProgram::GetUniformIndices(const dom::Sequence<nsString>& uniformNames,
949 dom::Nullable< nsTArray<GLuint> >& retval) const
950 {
951 const char funcName[] = "getUniformIndices";
952 if (!IsLinked()) {
953 mContext->ErrorInvalidOperation("%s: `program` must be linked.", funcName);
954 return;
955 }
956
957 size_t count = uniformNames.Length();
958 nsTArray<GLuint>& arr = retval.SetValue();
959
960 gl::GLContext* gl = mContext->GL();
961 gl->MakeCurrent();
962
963 for (size_t i = 0; i < count; i++) {
964 const NS_LossyConvertUTF16toASCII userName(uniformNames[i]);
965
966 nsCString mappedName;
967 size_t arrayIndex;
968 webgl::UniformInfo* info;
969 if (!LinkInfo()->FindUniform(userName, &mappedName, &arrayIndex, &info)) {
970 arr.AppendElement(LOCAL_GL_INVALID_INDEX);
971 continue;
972 }
973
974 const GLchar* const mappedNameBegin = mappedName.get();
975
976 GLuint index = LOCAL_GL_INVALID_INDEX;
977 gl->fGetUniformIndices(mGLName, 1, &mappedNameBegin, &index);
978 arr.AppendElement(index);
979 }
980 }
981
982 void
UniformBlockBinding(GLuint uniformBlockIndex,GLuint uniformBlockBinding) const983 WebGLProgram::UniformBlockBinding(GLuint uniformBlockIndex,
984 GLuint uniformBlockBinding) const
985 {
986 const char funcName[] = "getActiveUniformBlockName";
987 if (!IsLinked()) {
988 mContext->ErrorInvalidOperation("%s: `program` must be linked.", funcName);
989 return;
990 }
991
992 const auto& uniformBlocks = LinkInfo()->uniformBlocks;
993 if (uniformBlockIndex >= uniformBlocks.size()) {
994 mContext->ErrorInvalidValue("%s: Index %u invalid.", funcName, uniformBlockIndex);
995 return;
996 }
997 const auto& uniformBlock = uniformBlocks[uniformBlockIndex];
998
999 const auto& indexedBindings = mContext->mIndexedUniformBufferBindings;
1000 if (uniformBlockBinding >= indexedBindings.size()) {
1001 mContext->ErrorInvalidValue("%s: Binding %u invalid.", funcName,
1002 uniformBlockBinding);
1003 return;
1004 }
1005 const auto& indexedBinding = indexedBindings[uniformBlockBinding];
1006
1007 ////
1008
1009 gl::GLContext* gl = mContext->GL();
1010 gl->MakeCurrent();
1011 gl->fUniformBlockBinding(mGLName, uniformBlockIndex, uniformBlockBinding);
1012
1013 ////
1014
1015 uniformBlock->mBinding = &indexedBinding;
1016 }
1017
1018 bool
ValidateForLink()1019 WebGLProgram::ValidateForLink()
1020 {
1021 if (!mVertShader || !mVertShader->IsCompiled()) {
1022 mLinkLog.AssignLiteral("Must have a compiled vertex shader attached.");
1023 return false;
1024 }
1025
1026 if (!mFragShader || !mFragShader->IsCompiled()) {
1027 mLinkLog.AssignLiteral("Must have an compiled fragment shader attached.");
1028 return false;
1029 }
1030
1031 if (!mFragShader->CanLinkTo(mVertShader, &mLinkLog))
1032 return false;
1033
1034 const auto& gl = mContext->gl;
1035
1036 if (gl->WorkAroundDriverBugs() &&
1037 mContext->mIsMesa)
1038 {
1039 // Bug 777028: Mesa can't handle more than 16 samplers per program,
1040 // counting each array entry.
1041 size_t numSamplerUniforms_upperBound = mVertShader->CalcNumSamplerUniforms() +
1042 mFragShader->CalcNumSamplerUniforms();
1043 if (numSamplerUniforms_upperBound > 16) {
1044 mLinkLog.AssignLiteral("Programs with more than 16 samplers are disallowed on"
1045 " Mesa drivers to avoid crashing.");
1046 return false;
1047 }
1048
1049 // Bug 1203135: Mesa crashes internally if we exceed the reported maximum attribute count.
1050 if (mVertShader->NumAttributes() > mContext->MaxVertexAttribs()) {
1051 mLinkLog.AssignLiteral("Number of attributes exceeds Mesa's reported max"
1052 " attribute count.");
1053 return false;
1054 }
1055 }
1056
1057 return true;
1058 }
1059
1060 void
LinkProgram()1061 WebGLProgram::LinkProgram()
1062 {
1063 const char funcName[] = "linkProgram";
1064
1065 if (mNumActiveTFOs) {
1066 mContext->ErrorInvalidOperation("%s: Program is in-use by one or more active"
1067 " transform feedback objects.",
1068 funcName);
1069 return;
1070 }
1071
1072 mContext->MakeContextCurrent();
1073 mContext->InvalidateBufferFetching(); // we do it early in this function
1074 // as some of the validation changes program state
1075
1076 mLinkLog.Truncate();
1077 mMostRecentLinkInfo = nullptr;
1078
1079 if (!ValidateForLink()) {
1080 mContext->GenerateWarning("%s: %s", funcName, mLinkLog.BeginReading());
1081 return;
1082 }
1083
1084 // Bind the attrib locations.
1085 // This can't be done trivially, because we have to deal with mapped attrib names.
1086 for (const auto& pair : mNextLink_BoundAttribLocs) {
1087 const auto& name = pair.first;
1088 const auto& index = pair.second;
1089
1090 mVertShader->BindAttribLocation(mGLName, name, index);
1091 }
1092
1093 // Storage for transform feedback varyings before link.
1094 // (Work around for bug seen on nVidia drivers.)
1095 std::vector<std::string> scopedMappedTFVaryings;
1096
1097 if (mContext->IsWebGL2()) {
1098 mVertShader->MapTransformFeedbackVaryings(mNextLink_TransformFeedbackVaryings,
1099 &scopedMappedTFVaryings);
1100
1101 std::vector<const char*> driverVaryings;
1102 driverVaryings.reserve(scopedMappedTFVaryings.size());
1103 for (const auto& cur : scopedMappedTFVaryings) {
1104 driverVaryings.push_back(cur.c_str());
1105 }
1106
1107 mContext->gl->fTransformFeedbackVaryings(mGLName, driverVaryings.size(),
1108 driverVaryings.data(),
1109 mNextLink_TransformFeedbackBufferMode);
1110 }
1111
1112 LinkAndUpdate();
1113
1114 if (mMostRecentLinkInfo) {
1115 nsCString postLinkLog;
1116 if (ValidateAfterTentativeLink(&postLinkLog))
1117 return;
1118
1119 mMostRecentLinkInfo = nullptr;
1120 mLinkLog = postLinkLog;
1121 }
1122
1123 // Failed link.
1124 if (mContext->ShouldGenerateWarnings()) {
1125 // report shader/program infoLogs as warnings.
1126 // note that shader compilation errors can be deferred to linkProgram,
1127 // which is why we can't do anything in compileShader. In practice we could
1128 // report in compileShader the translation errors generated by ANGLE,
1129 // but it seems saner to keep a single way of obtaining shader infologs.
1130 if (!mLinkLog.IsEmpty()) {
1131 mContext->GenerateWarning("linkProgram: Failed to link, leaving the following"
1132 " log:\n%s\n",
1133 mLinkLog.BeginReading());
1134 }
1135 }
1136 }
1137
1138 static uint8_t
NumUsedLocationsByElemType(GLenum elemType)1139 NumUsedLocationsByElemType(GLenum elemType)
1140 {
1141 // GLES 3.0.4 p55
1142
1143 switch (elemType) {
1144 case LOCAL_GL_FLOAT_MAT2:
1145 case LOCAL_GL_FLOAT_MAT2x3:
1146 case LOCAL_GL_FLOAT_MAT2x4:
1147 return 2;
1148
1149 case LOCAL_GL_FLOAT_MAT3x2:
1150 case LOCAL_GL_FLOAT_MAT3:
1151 case LOCAL_GL_FLOAT_MAT3x4:
1152 return 3;
1153
1154 case LOCAL_GL_FLOAT_MAT4x2:
1155 case LOCAL_GL_FLOAT_MAT4x3:
1156 case LOCAL_GL_FLOAT_MAT4:
1157 return 4;
1158
1159 default:
1160 return 1;
1161 }
1162 }
1163
1164 static uint8_t
NumComponents(GLenum elemType)1165 NumComponents(GLenum elemType)
1166 {
1167 switch (elemType) {
1168 case LOCAL_GL_FLOAT:
1169 case LOCAL_GL_INT:
1170 case LOCAL_GL_UNSIGNED_INT:
1171 case LOCAL_GL_BOOL:
1172 return 1;
1173
1174 case LOCAL_GL_FLOAT_VEC2:
1175 case LOCAL_GL_INT_VEC2:
1176 case LOCAL_GL_UNSIGNED_INT_VEC2:
1177 case LOCAL_GL_BOOL_VEC2:
1178 return 2;
1179
1180 case LOCAL_GL_FLOAT_VEC3:
1181 case LOCAL_GL_INT_VEC3:
1182 case LOCAL_GL_UNSIGNED_INT_VEC3:
1183 case LOCAL_GL_BOOL_VEC3:
1184 return 3;
1185
1186 case LOCAL_GL_FLOAT_VEC4:
1187 case LOCAL_GL_INT_VEC4:
1188 case LOCAL_GL_UNSIGNED_INT_VEC4:
1189 case LOCAL_GL_BOOL_VEC4:
1190 case LOCAL_GL_FLOAT_MAT2:
1191 return 4;
1192
1193 case LOCAL_GL_FLOAT_MAT2x3:
1194 case LOCAL_GL_FLOAT_MAT3x2:
1195 return 6;
1196
1197 case LOCAL_GL_FLOAT_MAT2x4:
1198 case LOCAL_GL_FLOAT_MAT4x2:
1199 return 8;
1200
1201 case LOCAL_GL_FLOAT_MAT3:
1202 return 9;
1203
1204 case LOCAL_GL_FLOAT_MAT3x4:
1205 case LOCAL_GL_FLOAT_MAT4x3:
1206 return 12;
1207
1208 case LOCAL_GL_FLOAT_MAT4:
1209 return 16;
1210
1211 default:
1212 MOZ_CRASH("`elemType`");
1213 }
1214 }
1215
1216 bool
ValidateAfterTentativeLink(nsCString * const out_linkLog) const1217 WebGLProgram::ValidateAfterTentativeLink(nsCString* const out_linkLog) const
1218 {
1219 const auto& linkInfo = mMostRecentLinkInfo;
1220 const auto& gl = mContext->gl;
1221
1222 // Check if the attrib name conflicting to uniform name
1223 for (const auto& attrib : linkInfo->attribs) {
1224 const auto& attribName = attrib.mActiveInfo->mBaseUserName;
1225
1226 for (const auto& uniform : linkInfo->uniforms) {
1227 const auto& uniformName = uniform->mActiveInfo->mBaseUserName;
1228 if (attribName == uniformName) {
1229 *out_linkLog = nsPrintfCString("Attrib name conflicts with uniform name:"
1230 " %s",
1231 attribName.BeginReading());
1232 return false;
1233 }
1234 }
1235 }
1236
1237 std::map<uint32_t, const webgl::AttribInfo*> attribsByLoc;
1238 for (const auto& attrib : linkInfo->attribs) {
1239 if (attrib.mLoc == -1)
1240 continue;
1241
1242 const auto& elemType = attrib.mActiveInfo->mElemType;
1243 const auto numUsedLocs = NumUsedLocationsByElemType(elemType);
1244 for (uint32_t i = 0; i < numUsedLocs; i++) {
1245 const uint32_t usedLoc = attrib.mLoc + i;
1246
1247 const auto res = attribsByLoc.insert({usedLoc, &attrib});
1248 const bool& didInsert = res.second;
1249 if (!didInsert) {
1250 const auto& aliasingName = attrib.mActiveInfo->mBaseUserName;
1251 const auto& itrExisting = res.first;
1252 const auto& existingInfo = itrExisting->second;
1253 const auto& existingName = existingInfo->mActiveInfo->mBaseUserName;
1254 *out_linkLog = nsPrintfCString("Attrib \"%s\" aliases locations used by"
1255 " attrib \"%s\".",
1256 aliasingName.BeginReading(),
1257 existingName.BeginReading());
1258 return false;
1259 }
1260 }
1261 }
1262
1263 // Forbid:
1264 // * Unrecognized varying name
1265 // * Duplicate varying name
1266 // * Too many components for specified buffer mode
1267 if (mNextLink_TransformFeedbackVaryings.size()) {
1268 GLuint maxComponentsPerIndex = 0;
1269 switch (mNextLink_TransformFeedbackBufferMode) {
1270 case LOCAL_GL_INTERLEAVED_ATTRIBS:
1271 gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS,
1272 &maxComponentsPerIndex);
1273 break;
1274
1275 case LOCAL_GL_SEPARATE_ATTRIBS:
1276 gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS,
1277 &maxComponentsPerIndex);
1278 break;
1279
1280 default:
1281 MOZ_CRASH("`bufferMode`");
1282 }
1283
1284 std::vector<size_t> componentsPerVert;
1285 std::set<const WebGLActiveInfo*> alreadyUsed;
1286 for (const auto& wideUserName : mNextLink_TransformFeedbackVaryings) {
1287 if (!componentsPerVert.size() ||
1288 mNextLink_TransformFeedbackBufferMode == LOCAL_GL_SEPARATE_ATTRIBS)
1289 {
1290 componentsPerVert.push_back(0);
1291 }
1292
1293 ////
1294
1295 const WebGLActiveInfo* curInfo = nullptr;
1296 for (const auto& info : linkInfo->transformFeedbackVaryings) {
1297 const NS_ConvertASCIItoUTF16 info_wideUserName(info->mBaseUserName);
1298 if (info_wideUserName == wideUserName) {
1299 curInfo = info.get();
1300 break;
1301 }
1302 }
1303
1304 if (!curInfo) {
1305 const NS_LossyConvertUTF16toASCII asciiUserName(wideUserName);
1306 *out_linkLog = nsPrintfCString("Transform feedback varying \"%s\" not"
1307 " found.",
1308 asciiUserName.BeginReading());
1309 return false;
1310 }
1311
1312 const auto insertResPair = alreadyUsed.insert(curInfo);
1313 const auto& didInsert = insertResPair.second;
1314 if (!didInsert) {
1315 const NS_LossyConvertUTF16toASCII asciiUserName(wideUserName);
1316 *out_linkLog = nsPrintfCString("Transform feedback varying \"%s\""
1317 " specified twice.",
1318 asciiUserName.BeginReading());
1319 return false;
1320 }
1321
1322 ////
1323
1324 size_t varyingComponents = NumComponents(curInfo->mElemType);
1325 varyingComponents *= curInfo->mElemCount;
1326
1327 auto& totalComponentsForIndex = *(componentsPerVert.rbegin());
1328 totalComponentsForIndex += varyingComponents;
1329
1330 if (totalComponentsForIndex > maxComponentsPerIndex) {
1331 const NS_LossyConvertUTF16toASCII asciiUserName(wideUserName);
1332 *out_linkLog = nsPrintfCString("Transform feedback varying \"%s\""
1333 " pushed `componentsForIndex` over the"
1334 " limit of %u.",
1335 asciiUserName.BeginReading(),
1336 maxComponentsPerIndex);
1337 return false;
1338 }
1339 }
1340
1341 linkInfo->componentsPerTFVert.swap(componentsPerVert);
1342 }
1343
1344 return true;
1345 }
1346
1347 bool
UseProgram() const1348 WebGLProgram::UseProgram() const
1349 {
1350 const char funcName[] = "useProgram";
1351
1352 if (!mMostRecentLinkInfo) {
1353 mContext->ErrorInvalidOperation("%s: Program has not been successfully linked.",
1354 funcName);
1355 return false;
1356 }
1357
1358 if (mContext->mBoundTransformFeedback &&
1359 mContext->mBoundTransformFeedback->mIsActive &&
1360 !mContext->mBoundTransformFeedback->mIsPaused)
1361 {
1362 mContext->ErrorInvalidOperation("%s: Transform feedback active and not paused.",
1363 funcName);
1364 return false;
1365 }
1366
1367 mContext->MakeContextCurrent();
1368
1369 mContext->InvalidateBufferFetching();
1370
1371 mContext->gl->fUseProgram(mGLName);
1372 return true;
1373 }
1374
1375 void
ValidateProgram() const1376 WebGLProgram::ValidateProgram() const
1377 {
1378 mContext->MakeContextCurrent();
1379 gl::GLContext* gl = mContext->gl;
1380
1381 #ifdef XP_MACOSX
1382 // See bug 593867 for NVIDIA and bug 657201 for ATI. The latter is confirmed
1383 // with Mac OS 10.6.7.
1384 if (gl->WorkAroundDriverBugs()) {
1385 mContext->GenerateWarning("validateProgram: Implemented as a no-op on"
1386 " Mac to work around crashes.");
1387 return;
1388 }
1389 #endif
1390
1391 gl->fValidateProgram(mGLName);
1392 }
1393
1394
1395 ////////////////////////////////////////////////////////////////////////////////
1396
1397 void
LinkAndUpdate()1398 WebGLProgram::LinkAndUpdate()
1399 {
1400 mMostRecentLinkInfo = nullptr;
1401
1402 gl::GLContext* gl = mContext->gl;
1403 gl->fLinkProgram(mGLName);
1404
1405 // Grab the program log.
1406 GLuint logLenWithNull = 0;
1407 gl->fGetProgramiv(mGLName, LOCAL_GL_INFO_LOG_LENGTH, (GLint*)&logLenWithNull);
1408 if (logLenWithNull > 1) {
1409 mLinkLog.SetLength(logLenWithNull - 1);
1410 gl->fGetProgramInfoLog(mGLName, logLenWithNull, nullptr, mLinkLog.BeginWriting());
1411 } else {
1412 mLinkLog.SetLength(0);
1413 }
1414
1415 GLint ok = 0;
1416 gl->fGetProgramiv(mGLName, LOCAL_GL_LINK_STATUS, &ok);
1417 if (!ok)
1418 return;
1419
1420 mMostRecentLinkInfo = QueryProgramInfo(this, gl);
1421 MOZ_RELEASE_ASSERT(mMostRecentLinkInfo, "GFX: most recent link info not set.");
1422 }
1423
1424 bool
FindAttribUserNameByMappedName(const nsACString & mappedName,nsCString * const out_userName) const1425 WebGLProgram::FindAttribUserNameByMappedName(const nsACString& mappedName,
1426 nsCString* const out_userName) const
1427 {
1428 if (mVertShader->FindAttribUserNameByMappedName(mappedName, out_userName))
1429 return true;
1430
1431 return false;
1432 }
1433
1434 bool
FindVaryingByMappedName(const nsACString & mappedName,nsCString * const out_userName,bool * const out_isArray) const1435 WebGLProgram::FindVaryingByMappedName(const nsACString& mappedName,
1436 nsCString* const out_userName,
1437 bool* const out_isArray) const
1438 {
1439 if (mVertShader->FindVaryingByMappedName(mappedName, out_userName, out_isArray))
1440 return true;
1441
1442 return false;
1443 }
1444
1445
1446 bool
FindUniformByMappedName(const nsACString & mappedName,nsCString * const out_userName,bool * const out_isArray) const1447 WebGLProgram::FindUniformByMappedName(const nsACString& mappedName,
1448 nsCString* const out_userName,
1449 bool* const out_isArray) const
1450 {
1451 if (mVertShader->FindUniformByMappedName(mappedName, out_userName, out_isArray))
1452 return true;
1453
1454 if (mFragShader->FindUniformByMappedName(mappedName, out_userName, out_isArray))
1455 return true;
1456
1457 return false;
1458 }
1459
1460 void
TransformFeedbackVaryings(const dom::Sequence<nsString> & varyings,GLenum bufferMode)1461 WebGLProgram::TransformFeedbackVaryings(const dom::Sequence<nsString>& varyings,
1462 GLenum bufferMode)
1463 {
1464 const char funcName[] = "transformFeedbackVaryings";
1465
1466 const auto& gl = mContext->gl;
1467 gl->MakeCurrent();
1468
1469 switch (bufferMode) {
1470 case LOCAL_GL_INTERLEAVED_ATTRIBS:
1471 break;
1472
1473 case LOCAL_GL_SEPARATE_ATTRIBS:
1474 {
1475 GLuint maxAttribs = 0;
1476 gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS,
1477 &maxAttribs);
1478 if (varyings.Length() > maxAttribs) {
1479 mContext->ErrorInvalidValue("%s: Length of `varyings` exceeds %s.",
1480 funcName,
1481 "TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS");
1482 return;
1483 }
1484 }
1485 break;
1486
1487 default:
1488 mContext->ErrorInvalidEnum("%s: Bad `bufferMode`: 0x%04x.", funcName, bufferMode);
1489 return;
1490 }
1491
1492 ////
1493
1494 mNextLink_TransformFeedbackVaryings.assign(varyings.Elements(),
1495 varyings.Elements() + varyings.Length());
1496 mNextLink_TransformFeedbackBufferMode = bufferMode;
1497 }
1498
1499 already_AddRefed<WebGLActiveInfo>
GetTransformFeedbackVarying(GLuint index) const1500 WebGLProgram::GetTransformFeedbackVarying(GLuint index) const
1501 {
1502 // No docs in the WebGL 2 spec for this function. Taking the language for
1503 // getActiveAttrib, which states that the function returns null on any error.
1504 if (!IsLinked()) {
1505 mContext->ErrorInvalidOperation("getTransformFeedbackVarying: `program` must be "
1506 "linked.");
1507 return nullptr;
1508 }
1509
1510 if (index >= LinkInfo()->transformFeedbackVaryings.size()) {
1511 mContext->ErrorInvalidValue("getTransformFeedbackVarying: `index` is greater or "
1512 "equal to TRANSFORM_FEEDBACK_VARYINGS.");
1513 return nullptr;
1514 }
1515
1516 RefPtr<WebGLActiveInfo> ret = LinkInfo()->transformFeedbackVaryings[index];
1517 return ret.forget();
1518 }
1519
1520 bool
UnmapUniformBlockName(const nsCString & mappedName,nsCString * const out_userName) const1521 WebGLProgram::UnmapUniformBlockName(const nsCString& mappedName,
1522 nsCString* const out_userName) const
1523 {
1524 nsCString baseMappedName;
1525 bool isArray;
1526 size_t arrayIndex;
1527 if (!ParseName(mappedName, &baseMappedName, &isArray, &arrayIndex))
1528 return false;
1529
1530 nsCString baseUserName;
1531 if (!mVertShader->UnmapUniformBlockName(baseMappedName, &baseUserName) &&
1532 !mFragShader->UnmapUniformBlockName(baseMappedName, &baseUserName))
1533 {
1534 return false;
1535 }
1536
1537 AssembleName(baseUserName, isArray, arrayIndex, out_userName);
1538 return true;
1539 }
1540
1541 void
EnumerateFragOutputs(std::map<nsCString,const nsCString> & out_FragOutputs) const1542 WebGLProgram::EnumerateFragOutputs(std::map<nsCString, const nsCString> &out_FragOutputs) const
1543 {
1544 MOZ_ASSERT(mFragShader);
1545
1546 mFragShader->EnumerateFragOutputs(out_FragOutputs);
1547 }
1548
1549 ////////////////////////////////////////////////////////////////////////////////
1550
1551 bool
IsBaseName(const nsCString & name)1552 IsBaseName(const nsCString& name)
1553 {
1554 if (!name.Length())
1555 return true;
1556
1557 return name[name.Length() - 1] != ']'; // Doesn't end in ']'.
1558 }
1559
1560 bool
FindAttrib(const nsCString & userName,const webgl::AttribInfo ** const out) const1561 webgl::LinkedProgramInfo::FindAttrib(const nsCString& userName,
1562 const webgl::AttribInfo** const out) const
1563 {
1564 // VS inputs cannot be arrays or structures.
1565 // `userName` is thus always `baseUserName`.
1566 for (const auto& attrib : attribs) {
1567 if (attrib.mActiveInfo->mBaseUserName == userName) {
1568 *out = &attrib;
1569 return true;
1570 }
1571 }
1572
1573 return false;
1574 }
1575
1576 bool
FindUniform(const nsCString & userName,nsCString * const out_mappedName,size_t * const out_arrayIndex,webgl::UniformInfo ** const out_info) const1577 webgl::LinkedProgramInfo::FindUniform(const nsCString& userName,
1578 nsCString* const out_mappedName,
1579 size_t* const out_arrayIndex,
1580 webgl::UniformInfo** const out_info) const
1581 {
1582 nsCString baseUserName;
1583 bool isArray;
1584 size_t arrayIndex;
1585 if (!ParseName(userName, &baseUserName, &isArray, &arrayIndex))
1586 return false;
1587
1588 webgl::UniformInfo* info = nullptr;
1589 for (const auto& uniform : uniforms) {
1590 if (uniform->mActiveInfo->mBaseUserName == baseUserName) {
1591 info = uniform;
1592 break;
1593 }
1594 }
1595 if (!info)
1596 return false;
1597
1598 const auto& baseMappedName = info->mActiveInfo->mBaseMappedName;
1599 AssembleName(baseMappedName, isArray, arrayIndex, out_mappedName);
1600
1601 *out_arrayIndex = arrayIndex;
1602 *out_info = info;
1603 return true;
1604 }
1605
1606 bool
MapFragDataName(const nsCString & userName,nsCString * const out_mappedName) const1607 webgl::LinkedProgramInfo::MapFragDataName(const nsCString& userName,
1608 nsCString* const out_mappedName) const
1609 {
1610 // FS outputs can be arrays, but not structures.
1611
1612 if (!fragDataMap.size()) {
1613 // No mappings map from validation, so just forward it.
1614 *out_mappedName = userName;
1615 return true;
1616 }
1617
1618 nsCString baseUserName;
1619 bool isArray;
1620 size_t arrayIndex;
1621 if (!ParseName(userName, &baseUserName, &isArray, &arrayIndex))
1622 return false;
1623
1624 const auto itr = fragDataMap.find(baseUserName);
1625 if (itr == fragDataMap.end())
1626 return false;
1627
1628 const auto& baseMappedName = itr->second;
1629 AssembleName(baseMappedName, isArray, arrayIndex, out_mappedName);
1630 return true;
1631 }
1632
1633 ////////////////////////////////////////////////////////////////////////////////
1634
1635 JSObject*
WrapObject(JSContext * js,JS::Handle<JSObject * > givenProto)1636 WebGLProgram::WrapObject(JSContext* js, JS::Handle<JSObject*> givenProto)
1637 {
1638 return dom::WebGLProgramBinding::Wrap(js, this, givenProto);
1639 }
1640
1641 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLProgram, mVertShader, mFragShader)
1642
1643 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLProgram, AddRef)
1644 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLProgram, Release)
1645
1646 } // namespace mozilla
1647