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