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