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, &param);
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, &param);
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