Bug 1787199 [wpt PR 35620] - Add tests for `VisibilityStateEntry`, a=testonly
[gecko.git] / dom / canvas / WebGLProgram.cpp
blob586019603d8aa3417f6f501d6cf57d609ee44732
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "WebGLProgram.h"
8 #include "GLContext.h"
9 #include "mozilla/CheckedInt.h"
10 #include "mozilla/dom/WebGL2RenderingContextBinding.h"
11 #include "mozilla/dom/WebGLRenderingContextBinding.h"
12 #include "mozilla/gfx/Logging.h"
13 #include "mozilla/RefPtr.h"
14 #include "nsPrintfCString.h"
15 #include "WebGLBuffer.h"
16 #include "WebGLContext.h"
17 #include "WebGLShader.h"
18 #include "WebGLShaderValidator.h"
19 #include "WebGLTransformFeedback.h"
20 #include "WebGLValidateStrings.h"
21 #include "WebGLVertexArray.h"
23 namespace mozilla {
25 static bool IsShadowSampler(const GLenum elemType) {
26 switch (elemType) {
27 case LOCAL_GL_SAMPLER_2D_SHADOW:
28 case LOCAL_GL_SAMPLER_CUBE_SHADOW:
29 case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
30 return true;
31 default:
32 return false;
36 static Maybe<webgl::TextureBaseType> SamplerBaseType(const GLenum elemType) {
37 switch (elemType) {
38 case LOCAL_GL_SAMPLER_2D:
39 case LOCAL_GL_SAMPLER_3D:
40 case LOCAL_GL_SAMPLER_CUBE:
41 case LOCAL_GL_SAMPLER_2D_ARRAY:
42 case LOCAL_GL_SAMPLER_2D_SHADOW:
43 case LOCAL_GL_SAMPLER_CUBE_SHADOW:
44 case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
45 return Some(webgl::TextureBaseType::Float);
47 case LOCAL_GL_INT_SAMPLER_2D:
48 case LOCAL_GL_INT_SAMPLER_3D:
49 case LOCAL_GL_INT_SAMPLER_CUBE:
50 case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
51 return Some(webgl::TextureBaseType::Int);
53 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
54 case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
55 case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
56 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
57 return Some(webgl::TextureBaseType::UInt);
59 default:
60 return {};
64 //////////
66 static webgl::TextureBaseType FragOutputBaseType(const GLenum type) {
67 switch (type) {
68 case LOCAL_GL_FLOAT:
69 case LOCAL_GL_FLOAT_VEC2:
70 case LOCAL_GL_FLOAT_VEC3:
71 case LOCAL_GL_FLOAT_VEC4:
72 return webgl::TextureBaseType::Float;
74 case LOCAL_GL_INT:
75 case LOCAL_GL_INT_VEC2:
76 case LOCAL_GL_INT_VEC3:
77 case LOCAL_GL_INT_VEC4:
78 return webgl::TextureBaseType::Int;
80 case LOCAL_GL_UNSIGNED_INT:
81 case LOCAL_GL_UNSIGNED_INT_VEC2:
82 case LOCAL_GL_UNSIGNED_INT_VEC3:
83 case LOCAL_GL_UNSIGNED_INT_VEC4:
84 return webgl::TextureBaseType::UInt;
86 default:
87 break;
90 const auto& str = EnumString(type);
91 gfxCriticalError() << "Unhandled enum for FragOutputBaseType: "
92 << str.c_str();
93 return webgl::TextureBaseType::Float;
96 // -----------------------------------------
98 namespace webgl {
100 void UniformAs1fv(gl::GLContext& gl, GLint location, GLsizei count,
101 bool transpose, const void* any) {
102 gl.fUniform1fv(location, count, static_cast<const float*>(any));
104 void UniformAs2fv(gl::GLContext& gl, GLint location, GLsizei count,
105 bool transpose, const void* any) {
106 gl.fUniform2fv(location, count, static_cast<const float*>(any));
108 void UniformAs3fv(gl::GLContext& gl, GLint location, GLsizei count,
109 bool transpose, const void* any) {
110 gl.fUniform3fv(location, count, static_cast<const float*>(any));
112 void UniformAs4fv(gl::GLContext& gl, GLint location, GLsizei count,
113 bool transpose, const void* any) {
114 gl.fUniform4fv(location, count, static_cast<const float*>(any));
117 void UniformAs1iv(gl::GLContext& gl, GLint location, GLsizei count,
118 bool transpose, const void* any) {
119 gl.fUniform1iv(location, count, static_cast<const int32_t*>(any));
121 void UniformAs2iv(gl::GLContext& gl, GLint location, GLsizei count,
122 bool transpose, const void* any) {
123 gl.fUniform2iv(location, count, static_cast<const int32_t*>(any));
125 void UniformAs3iv(gl::GLContext& gl, GLint location, GLsizei count,
126 bool transpose, const void* any) {
127 gl.fUniform3iv(location, count, static_cast<const int32_t*>(any));
129 void UniformAs4iv(gl::GLContext& gl, GLint location, GLsizei count,
130 bool transpose, const void* any) {
131 gl.fUniform4iv(location, count, static_cast<const int32_t*>(any));
134 void UniformAs1uiv(gl::GLContext& gl, GLint location, GLsizei count,
135 bool transpose, const void* any) {
136 gl.fUniform1uiv(location, count, static_cast<const uint32_t*>(any));
138 void UniformAs2uiv(gl::GLContext& gl, GLint location, GLsizei count,
139 bool transpose, const void* any) {
140 gl.fUniform2uiv(location, count, static_cast<const uint32_t*>(any));
142 void UniformAs3uiv(gl::GLContext& gl, GLint location, GLsizei count,
143 bool transpose, const void* any) {
144 gl.fUniform3uiv(location, count, static_cast<const uint32_t*>(any));
146 void UniformAs4uiv(gl::GLContext& gl, GLint location, GLsizei count,
147 bool transpose, const void* any) {
148 gl.fUniform4uiv(location, count, static_cast<const uint32_t*>(any));
151 void UniformAsMatrix2x2fv(gl::GLContext& gl, GLint location, GLsizei count,
152 bool transpose, const void* any) {
153 gl.fUniformMatrix2fv(location, count, transpose,
154 static_cast<const float*>(any));
156 void UniformAsMatrix2x3fv(gl::GLContext& gl, GLint location, GLsizei count,
157 bool transpose, const void* any) {
158 gl.fUniformMatrix2x3fv(location, count, transpose,
159 static_cast<const float*>(any));
161 void UniformAsMatrix2x4fv(gl::GLContext& gl, GLint location, GLsizei count,
162 bool transpose, const void* any) {
163 gl.fUniformMatrix2x4fv(location, count, transpose,
164 static_cast<const float*>(any));
167 void UniformAsMatrix3x2fv(gl::GLContext& gl, GLint location, GLsizei count,
168 bool transpose, const void* any) {
169 gl.fUniformMatrix3x2fv(location, count, transpose,
170 static_cast<const float*>(any));
172 void UniformAsMatrix3x3fv(gl::GLContext& gl, GLint location, GLsizei count,
173 bool transpose, const void* any) {
174 gl.fUniformMatrix3fv(location, count, transpose,
175 static_cast<const float*>(any));
177 void UniformAsMatrix3x4fv(gl::GLContext& gl, GLint location, GLsizei count,
178 bool transpose, const void* any) {
179 gl.fUniformMatrix3x4fv(location, count, transpose,
180 static_cast<const float*>(any));
183 void UniformAsMatrix4x2fv(gl::GLContext& gl, GLint location, GLsizei count,
184 bool transpose, const void* any) {
185 gl.fUniformMatrix4x2fv(location, count, transpose,
186 static_cast<const float*>(any));
188 void UniformAsMatrix4x3fv(gl::GLContext& gl, GLint location, GLsizei count,
189 bool transpose, const void* any) {
190 gl.fUniformMatrix4x3fv(location, count, transpose,
191 static_cast<const float*>(any));
193 void UniformAsMatrix4x4fv(gl::GLContext& gl, GLint location, GLsizei count,
194 bool transpose, const void* any) {
195 gl.fUniformMatrix4fv(location, count, transpose,
196 static_cast<const float*>(any));
199 // -
201 static bool EndsWith(const std::string& str, const std::string& needle) {
202 if (str.length() < needle.length()) return false;
203 return str.compare(str.length() - needle.length(), needle.length(), needle) ==
207 webgl::ActiveUniformValidationInfo webgl::ActiveUniformValidationInfo::Make(
208 const webgl::ActiveUniformInfo& info) {
209 auto ret = webgl::ActiveUniformValidationInfo{info};
210 ret.isArray = EndsWith(info.name, "[0]");
212 switch (info.elemType) {
213 case LOCAL_GL_FLOAT:
214 ret.channelsPerElem = 1;
215 ret.pfn = &UniformAs1fv;
216 break;
217 case LOCAL_GL_FLOAT_VEC2:
218 ret.channelsPerElem = 2;
219 ret.pfn = &UniformAs2fv;
220 break;
221 case LOCAL_GL_FLOAT_VEC3:
222 ret.channelsPerElem = 3;
223 ret.pfn = &UniformAs3fv;
224 break;
225 case LOCAL_GL_FLOAT_VEC4:
226 ret.channelsPerElem = 4;
227 ret.pfn = &UniformAs4fv;
228 break;
230 case LOCAL_GL_SAMPLER_2D:
231 case LOCAL_GL_SAMPLER_3D:
232 case LOCAL_GL_SAMPLER_CUBE:
233 case LOCAL_GL_SAMPLER_2D_SHADOW:
234 case LOCAL_GL_SAMPLER_2D_ARRAY:
235 case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
236 case LOCAL_GL_SAMPLER_CUBE_SHADOW:
237 case LOCAL_GL_INT_SAMPLER_2D:
238 case LOCAL_GL_INT_SAMPLER_3D:
239 case LOCAL_GL_INT_SAMPLER_CUBE:
240 case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
241 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
242 case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
243 case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
244 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
245 case LOCAL_GL_BOOL:
246 case LOCAL_GL_INT:
247 ret.channelsPerElem = 1;
248 ret.pfn = &UniformAs1iv;
249 break;
250 case LOCAL_GL_BOOL_VEC2:
251 case LOCAL_GL_INT_VEC2:
252 ret.channelsPerElem = 2;
253 ret.pfn = &UniformAs2iv;
254 break;
255 case LOCAL_GL_BOOL_VEC3:
256 case LOCAL_GL_INT_VEC3:
257 ret.channelsPerElem = 3;
258 ret.pfn = &UniformAs3iv;
259 break;
260 case LOCAL_GL_BOOL_VEC4:
261 case LOCAL_GL_INT_VEC4:
262 ret.channelsPerElem = 4;
263 ret.pfn = &UniformAs4iv;
264 break;
266 case LOCAL_GL_UNSIGNED_INT:
267 ret.channelsPerElem = 1;
268 ret.pfn = &UniformAs1uiv;
269 break;
270 case LOCAL_GL_UNSIGNED_INT_VEC2:
271 ret.channelsPerElem = 2;
272 ret.pfn = &UniformAs2uiv;
273 break;
274 case LOCAL_GL_UNSIGNED_INT_VEC3:
275 ret.channelsPerElem = 3;
276 ret.pfn = &UniformAs3uiv;
277 break;
278 case LOCAL_GL_UNSIGNED_INT_VEC4:
279 ret.channelsPerElem = 4;
280 ret.pfn = &UniformAs4uiv;
281 break;
283 // -
285 case LOCAL_GL_FLOAT_MAT2:
286 ret.channelsPerElem = 2 * 2;
287 ret.pfn = &UniformAsMatrix2x2fv;
288 break;
289 case LOCAL_GL_FLOAT_MAT2x3:
290 ret.channelsPerElem = 2 * 3;
291 ret.pfn = &UniformAsMatrix2x3fv;
292 break;
293 case LOCAL_GL_FLOAT_MAT2x4:
294 ret.channelsPerElem = 2 * 4;
295 ret.pfn = &UniformAsMatrix2x4fv;
296 break;
298 case LOCAL_GL_FLOAT_MAT3x2:
299 ret.channelsPerElem = 3 * 2;
300 ret.pfn = &UniformAsMatrix3x2fv;
301 break;
302 case LOCAL_GL_FLOAT_MAT3:
303 ret.channelsPerElem = 3 * 3;
304 ret.pfn = &UniformAsMatrix3x3fv;
305 break;
306 case LOCAL_GL_FLOAT_MAT3x4:
307 ret.channelsPerElem = 3 * 4;
308 ret.pfn = &UniformAsMatrix3x4fv;
309 break;
311 case LOCAL_GL_FLOAT_MAT4x2:
312 ret.channelsPerElem = 4 * 2;
313 ret.pfn = &UniformAsMatrix4x2fv;
314 break;
315 case LOCAL_GL_FLOAT_MAT4x3:
316 ret.channelsPerElem = 4 * 3;
317 ret.pfn = &UniformAsMatrix4x3fv;
318 break;
319 case LOCAL_GL_FLOAT_MAT4:
320 ret.channelsPerElem = 4 * 4;
321 ret.pfn = &UniformAsMatrix4x4fv;
322 break;
324 default:
325 gfxCriticalError() << "Bad `elemType`: " << EnumString(info.elemType);
326 MOZ_CRASH("`elemType`");
328 return ret;
331 } // namespace webgl
333 // -------------------------
335 //#define DUMP_SHADERVAR_MAPPINGS
337 RefPtr<const webgl::LinkedProgramInfo> QueryProgramInfo(WebGLProgram* prog,
338 gl::GLContext* gl) {
339 WebGLContext* const webgl = prog->mContext;
341 RefPtr<webgl::LinkedProgramInfo> info(new webgl::LinkedProgramInfo(prog));
343 // Frag outputs
346 const auto& fragShader = prog->FragShader();
347 const auto& compileResults = fragShader->CompileResults();
348 const auto version = compileResults->mShaderVersion;
350 const auto fnAddInfo = [&](const webgl::FragOutputInfo& x) {
351 info->hasOutput[x.loc] = true;
352 info->fragOutputs.insert({x.loc, x});
355 if (version == 300) {
356 for (const auto& cur : compileResults->mOutputVariables) {
357 auto loc = cur.location;
358 if (loc == -1) loc = 0;
360 const auto info =
361 webgl::FragOutputInfo{uint8_t(loc), cur.name, cur.mappedName,
362 FragOutputBaseType(cur.type)};
363 if (!cur.isArray()) {
364 fnAddInfo(info);
365 continue;
367 MOZ_ASSERT(cur.arraySizes.size() == 1);
368 for (uint32_t i = 0; i < cur.arraySizes[0]; ++i) {
369 const auto indexStr = std::string("[") + std::to_string(i) + "]";
371 const auto userName = info.userName + indexStr;
372 const auto mappedName = info.mappedName + indexStr;
374 const auto indexedInfo = webgl::FragOutputInfo{
375 uint8_t(info.loc + i), userName, mappedName, info.baseType};
376 fnAddInfo(indexedInfo);
379 } else {
380 // ANGLE's translator doesn't tell us about non-user frag outputs. :(
382 const auto& translatedSource = compileResults->mObjectCode;
383 uint32_t drawBuffers = 1;
384 if (translatedSource.find("(gl_FragData[1]") != std::string::npos ||
385 translatedSource.find("(webgl_FragData[1]") != std::string::npos) {
386 // The matching with the leading '(' prevents cleverly-named user vars
387 // breaking this. Since ANGLE initializes all outputs, if this is an MRT
388 // shader, FragData[1] will be present. FragData[0] is valid for non-MRT
389 // shaders.
390 drawBuffers = webgl->GLMaxDrawBuffers();
391 } else if (translatedSource.find("(gl_FragColor") == std::string::npos &&
392 translatedSource.find("(webgl_FragColor") ==
393 std::string::npos &&
394 translatedSource.find("(gl_FragData") == std::string::npos &&
395 translatedSource.find("(webgl_FragData") ==
396 std::string::npos) {
397 // We have to support no-color-output shaders?
398 drawBuffers = 0;
401 for (uint32_t i = 0; i < drawBuffers; ++i) {
402 const auto name = std::string("gl_FragData[") + std::to_string(i) + "]";
403 const auto info = webgl::FragOutputInfo{uint8_t(i), name, name,
404 webgl::TextureBaseType::Float};
405 fnAddInfo(info);
410 const auto& vertShader = prog->VertShader();
411 const auto& vertCompileResults = vertShader->CompileResults();
412 const auto numViews = vertCompileResults->mVertexShaderNumViews;
413 if (numViews != -1) {
414 info->zLayerCount = AssertedCast<uint8_t>(numViews);
417 // -
419 auto& nameMap = info->nameMap;
421 const auto fnAccum = [&](WebGLShader& shader) {
422 const auto& compRes = shader.CompileResults();
423 for (const auto& pair : compRes->mNameMap) {
424 nameMap.insert(pair);
427 fnAccum(*prog->VertShader());
428 fnAccum(*prog->FragShader());
430 // -
432 std::unordered_map<std::string, std::string> nameUnmap;
433 for (const auto& pair : nameMap) {
434 nameUnmap.insert({pair.second, pair.first});
437 info->active =
438 GetLinkActiveInfo(*gl, prog->mGLName, webgl->IsWebGL2(), nameUnmap);
440 // -
442 for (const auto& attrib : info->active.activeAttribs) {
443 if (attrib.location == 0) {
444 info->attrib0Active = true;
445 break;
449 info->webgl_gl_VertexID_Offset =
450 gl->fGetUniformLocation(prog->mGLName, "webgl_gl_VertexID_Offset");
452 // -
454 for (const auto& uniform : info->active.activeUniforms) {
455 const auto& elemType = uniform.elemType;
456 webgl::SamplerUniformInfo* samplerInfo = nullptr;
457 const auto baseType = SamplerBaseType(elemType);
458 if (baseType) {
459 const bool isShadowSampler = IsShadowSampler(elemType);
461 auto* texList = &webgl->mBound2DTextures;
463 switch (elemType) {
464 case LOCAL_GL_SAMPLER_2D:
465 case LOCAL_GL_SAMPLER_2D_SHADOW:
466 case LOCAL_GL_INT_SAMPLER_2D:
467 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
468 break;
470 case LOCAL_GL_SAMPLER_CUBE:
471 case LOCAL_GL_SAMPLER_CUBE_SHADOW:
472 case LOCAL_GL_INT_SAMPLER_CUBE:
473 case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
474 texList = &webgl->mBoundCubeMapTextures;
475 break;
477 case LOCAL_GL_SAMPLER_3D:
478 case LOCAL_GL_INT_SAMPLER_3D:
479 case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
480 texList = &webgl->mBound3DTextures;
481 break;
483 case LOCAL_GL_SAMPLER_2D_ARRAY:
484 case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
485 case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
486 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
487 texList = &webgl->mBound2DArrayTextures;
488 break;
491 auto curInfo = std::unique_ptr<webgl::SamplerUniformInfo>(
492 new webgl::SamplerUniformInfo{*texList, *baseType, isShadowSampler});
493 curInfo->texUnits.resize(uniform.elemCount);
494 samplerInfo = curInfo.get();
495 info->samplerUniforms.push_back(std::move(curInfo));
498 const auto valInfo = webgl::ActiveUniformValidationInfo::Make(uniform);
500 for (const auto& pair : uniform.locByIndex) {
501 info->locationMap.insert(
502 {pair.second, {valInfo, pair.first, samplerInfo}});
506 // -
509 const auto& activeBlocks = info->active.activeUniformBlocks;
510 info->uniformBlocks.reserve(activeBlocks.size());
511 for (const auto& cur : activeBlocks) {
512 const auto curInfo = webgl::UniformBlockInfo{
513 cur, &webgl->mIndexedUniformBufferBindings[0]};
514 info->uniformBlocks.push_back(curInfo);
518 return info;
521 ////////////////////////////////////////////////////////////////////////////////
523 webgl::LinkedProgramInfo::LinkedProgramInfo(WebGLProgram* prog)
524 : prog(prog),
525 transformFeedbackBufferMode(prog->mNextLink_TransformFeedbackBufferMode) {
528 webgl::LinkedProgramInfo::~LinkedProgramInfo() = default;
530 webgl::AttribBaseType webgl::ToAttribBaseType(const GLenum elemType) {
531 switch (elemType) {
532 case LOCAL_GL_BOOL:
533 case LOCAL_GL_BOOL_VEC2:
534 case LOCAL_GL_BOOL_VEC3:
535 case LOCAL_GL_BOOL_VEC4:
536 return webgl::AttribBaseType::Boolean;
538 case LOCAL_GL_FLOAT:
539 case LOCAL_GL_FLOAT_VEC2:
540 case LOCAL_GL_FLOAT_VEC3:
541 case LOCAL_GL_FLOAT_VEC4:
542 case LOCAL_GL_FLOAT_MAT2:
543 case LOCAL_GL_FLOAT_MAT2x3:
544 case LOCAL_GL_FLOAT_MAT3x2:
545 case LOCAL_GL_FLOAT_MAT2x4:
546 case LOCAL_GL_FLOAT_MAT4x2:
547 case LOCAL_GL_FLOAT_MAT3:
548 case LOCAL_GL_FLOAT_MAT3x4:
549 case LOCAL_GL_FLOAT_MAT4x3:
550 case LOCAL_GL_FLOAT_MAT4:
551 return webgl::AttribBaseType::Float;
553 case LOCAL_GL_INT:
554 case LOCAL_GL_INT_VEC2:
555 case LOCAL_GL_INT_VEC3:
556 case LOCAL_GL_INT_VEC4:
557 case LOCAL_GL_SAMPLER_2D:
558 case LOCAL_GL_SAMPLER_3D:
559 case LOCAL_GL_SAMPLER_CUBE:
560 case LOCAL_GL_SAMPLER_2D_SHADOW:
561 case LOCAL_GL_SAMPLER_2D_ARRAY:
562 case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
563 case LOCAL_GL_SAMPLER_CUBE_SHADOW:
564 case LOCAL_GL_INT_SAMPLER_2D:
565 case LOCAL_GL_INT_SAMPLER_3D:
566 case LOCAL_GL_INT_SAMPLER_CUBE:
567 case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
568 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
569 case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
570 case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
571 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
572 return webgl::AttribBaseType::Int;
574 case LOCAL_GL_UNSIGNED_INT:
575 case LOCAL_GL_UNSIGNED_INT_VEC2:
576 case LOCAL_GL_UNSIGNED_INT_VEC3:
577 case LOCAL_GL_UNSIGNED_INT_VEC4:
578 return webgl::AttribBaseType::Uint;
580 default:
581 gfxCriticalError() << "Bad `elemType`: " << EnumString(elemType);
582 MOZ_CRASH("`elemType`");
586 const char* webgl::ToString(const webgl::AttribBaseType x) {
587 switch (x) {
588 case webgl::AttribBaseType::Float:
589 return "FLOAT";
590 case webgl::AttribBaseType::Int:
591 return "INT";
592 case webgl::AttribBaseType::Uint:
593 return "UINT";
594 case webgl::AttribBaseType::Boolean:
595 return "BOOL";
597 MOZ_CRASH("pacify gcc6 warning");
600 const char* webgl::ToString(const webgl::UniformBaseType x) {
601 switch (x) {
602 case webgl::UniformBaseType::Float:
603 return "FLOAT";
604 case webgl::UniformBaseType::Int:
605 return "INT";
606 case webgl::UniformBaseType::Uint:
607 return "UINT";
609 MOZ_CRASH("pacify gcc6 warning");
612 const webgl::CachedDrawFetchLimits*
613 webgl::LinkedProgramInfo::GetDrawFetchLimits() const {
614 const auto& webgl = prog->mContext;
615 const auto& vao = webgl->mBoundVertexArray;
618 // We have to ensure that every enabled attrib array (not just the active
619 // ones) has a non-null buffer.
620 const auto badIndex = vao->GetAttribIsArrayWithNullBuffer();
621 if (badIndex) {
622 webgl->ErrorInvalidOperation(
623 "Vertex attrib array %u is enabled but"
624 " has no buffer bound.",
625 *badIndex);
626 return nullptr;
630 const auto& activeAttribs = active.activeAttribs;
632 webgl::CachedDrawFetchLimits fetchLimits;
633 fetchLimits.usedBuffers =
634 std::move(mScratchFetchLimits.usedBuffers); // Avoid realloc.
635 fetchLimits.usedBuffers.clear();
636 fetchLimits.usedBuffers.reserve(activeAttribs.size());
638 bool hasActiveAttrib = false;
639 bool hasActiveDivisor0 = false;
641 for (const auto& progAttrib : activeAttribs) {
642 const auto& loc = progAttrib.location;
643 if (loc == -1) continue;
644 hasActiveAttrib |= true;
646 const auto& binding = vao->AttribBinding(loc);
647 const auto& buffer = binding.buffer;
648 const auto& layout = binding.layout;
649 hasActiveDivisor0 |= (layout.divisor == 0);
651 webgl::AttribBaseType attribDataBaseType;
652 if (layout.isArray) {
653 MOZ_ASSERT(buffer);
654 fetchLimits.usedBuffers.push_back(
655 {buffer.get(), static_cast<uint32_t>(loc)});
657 attribDataBaseType = layout.baseType;
659 const auto availBytes = buffer->ByteLength();
660 const auto availElems = AvailGroups(availBytes, layout.byteOffset,
661 layout.byteSize, layout.byteStride);
662 if (layout.divisor) {
663 const auto availInstances =
664 CheckedInt<uint64_t>(availElems) * layout.divisor;
665 if (availInstances.isValid()) {
666 fetchLimits.maxInstances =
667 std::min(fetchLimits.maxInstances, availInstances.value());
668 } // If not valid, it overflowed too large, so we're super safe.
669 } else {
670 fetchLimits.maxVerts = std::min(fetchLimits.maxVerts, availElems);
672 } else {
673 attribDataBaseType = webgl->mGenericVertexAttribTypes[loc];
676 const auto& progBaseType = progAttrib.baseType;
677 if ((attribDataBaseType != progBaseType) &&
678 (progBaseType != webgl::AttribBaseType::Boolean)) {
679 const auto& dataType = ToString(attribDataBaseType);
680 const auto& progType = ToString(progBaseType);
681 webgl->ErrorInvalidOperation(
682 "Vertex attrib %u requires data of type %s,"
683 " but is being supplied with type %s.",
684 loc, progType, dataType);
685 return nullptr;
689 if (!webgl->IsWebGL2() && hasActiveAttrib && !hasActiveDivisor0) {
690 webgl->ErrorInvalidOperation(
691 "One active vertex attrib (if any are active)"
692 " must have a divisor of 0.");
693 return nullptr;
696 // -
698 mScratchFetchLimits = std::move(fetchLimits);
699 return &mScratchFetchLimits;
702 ////////////////////////////////////////////////////////////////////////////////
703 // WebGLProgram
705 WebGLProgram::WebGLProgram(WebGLContext* webgl)
706 : WebGLContextBoundObject(webgl),
707 mGLName(webgl->gl->fCreateProgram()),
708 mNumActiveTFOs(0),
709 mNextLink_TransformFeedbackBufferMode(LOCAL_GL_INTERLEAVED_ATTRIBS) {}
711 WebGLProgram::~WebGLProgram() {
712 mVertShader = nullptr;
713 mFragShader = nullptr;
715 mMostRecentLinkInfo = nullptr;
717 if (!mContext) return;
718 mContext->gl->fDeleteProgram(mGLName);
721 ////////////////////////////////////////////////////////////////////////////////
722 // GL funcs
724 void WebGLProgram::AttachShader(WebGLShader& shader) {
725 RefPtr<WebGLShader>* shaderSlot = nullptr;
726 switch (shader.mType) {
727 case LOCAL_GL_VERTEX_SHADER:
728 shaderSlot = &mVertShader;
729 break;
730 case LOCAL_GL_FRAGMENT_SHADER:
731 shaderSlot = &mFragShader;
732 break;
734 MOZ_ASSERT(shaderSlot);
736 *shaderSlot = &shader;
738 mContext->gl->fAttachShader(mGLName, shader.mGLName);
741 void WebGLProgram::BindAttribLocation(GLuint loc, const std::string& name) {
742 const auto err = CheckGLSLVariableName(mContext->IsWebGL2(), name);
743 if (err) {
744 mContext->GenerateError(err->type, "%s", err->info.c_str());
745 return;
748 if (loc >= mContext->MaxVertexAttribs()) {
749 mContext->ErrorInvalidValue(
750 "`location` must be less than"
751 " MAX_VERTEX_ATTRIBS.");
752 return;
755 if (name.find("gl_") == 0) {
756 mContext->ErrorInvalidOperation(
757 "Can't set the location of a"
758 " name that starts with 'gl_'.");
759 return;
762 auto res = mNextLink_BoundAttribLocs.insert({name, loc});
764 const auto& wasInserted = res.second;
765 if (!wasInserted) {
766 const auto& itr = res.first;
767 itr->second = loc;
771 void WebGLProgram::DetachShader(const WebGLShader& shader) {
772 RefPtr<WebGLShader>* shaderSlot = nullptr;
773 switch (shader.mType) {
774 case LOCAL_GL_VERTEX_SHADER:
775 shaderSlot = &mVertShader;
776 break;
777 case LOCAL_GL_FRAGMENT_SHADER:
778 shaderSlot = &mFragShader;
779 break;
781 MOZ_ASSERT(shaderSlot);
783 if (*shaderSlot != &shader) return;
785 *shaderSlot = nullptr;
787 mContext->gl->fDetachShader(mGLName, shader.mGLName);
790 void WebGLProgram::UniformBlockBinding(GLuint uniformBlockIndex,
791 GLuint uniformBlockBinding) const {
792 if (!IsLinked()) {
793 mContext->ErrorInvalidOperation("`program` must be linked.");
794 return;
797 auto& uniformBlocks = LinkInfo()->uniformBlocks;
798 if (uniformBlockIndex >= uniformBlocks.size()) {
799 mContext->ErrorInvalidValue("Index %u invalid.", uniformBlockIndex);
800 return;
802 auto& uniformBlock = uniformBlocks[uniformBlockIndex];
804 const auto& indexedBindings = mContext->mIndexedUniformBufferBindings;
805 if (uniformBlockBinding >= indexedBindings.size()) {
806 mContext->ErrorInvalidValue("Binding %u invalid.", uniformBlockBinding);
807 return;
809 const auto& indexedBinding = indexedBindings[uniformBlockBinding];
811 ////
813 gl::GLContext* gl = mContext->GL();
814 gl->fUniformBlockBinding(mGLName, uniformBlockIndex, uniformBlockBinding);
816 ////
818 uniformBlock.binding = &indexedBinding;
821 bool WebGLProgram::ValidateForLink() {
822 const auto AppendCompileLog = [&](const WebGLShader* const shader) {
823 if (!shader) {
824 mLinkLog += " Missing shader.";
825 return;
827 mLinkLog += "\nSHADER_INFO_LOG:\n";
828 mLinkLog += shader->CompileLog();
831 if (!mVertShader || !mVertShader->IsCompiled()) {
832 mLinkLog = "Must have a compiled vertex shader attached:";
833 AppendCompileLog(mVertShader);
834 return false;
836 const auto& vertInfo = *mVertShader->CompileResults();
838 if (!mFragShader || !mFragShader->IsCompiled()) {
839 mLinkLog = "Must have a compiled fragment shader attached:";
840 AppendCompileLog(mFragShader);
841 return false;
843 const auto& fragInfo = *mFragShader->CompileResults();
845 nsCString errInfo;
846 if (!fragInfo.CanLinkTo(vertInfo, &errInfo)) {
847 mLinkLog = errInfo.BeginReading();
848 return false;
851 const auto& gl = mContext->gl;
853 if (gl->WorkAroundDriverBugs() && mContext->mIsMesa) {
854 // Bug 1203135: Mesa crashes internally if we exceed the reported maximum
855 // attribute count.
856 if (mVertShader->NumAttributes() > mContext->MaxVertexAttribs()) {
857 mLinkLog =
858 "Number of attributes exceeds Mesa's reported max"
859 " attribute count.";
860 return false;
864 return true;
867 void WebGLProgram::LinkProgram() {
868 if (mNumActiveTFOs) {
869 mContext->ErrorInvalidOperation(
870 "Program is in-use by one or more active"
871 " transform feedback objects.");
872 return;
875 // as some of the validation changes program state
877 mLinkLog = {};
878 mMostRecentLinkInfo = nullptr;
880 if (!ValidateForLink()) {
881 mContext->GenerateWarning("%s", mLinkLog.c_str());
882 return;
885 // Bind the attrib locations.
886 // This can't be done trivially, because we have to deal with mapped attrib
887 // names.
888 for (const auto& pair : mNextLink_BoundAttribLocs) {
889 const auto& name = pair.first;
890 const auto& index = pair.second;
892 mVertShader->BindAttribLocation(mGLName, name, index);
895 // Storage for transform feedback varyings before link.
896 // (Work around for bug seen on nVidia drivers.)
897 std::vector<std::string> scopedMappedTFVaryings;
899 if (mContext->IsWebGL2()) {
900 mVertShader->MapTransformFeedbackVaryings(
901 mNextLink_TransformFeedbackVaryings, &scopedMappedTFVaryings);
903 std::vector<const char*> driverVaryings;
904 driverVaryings.reserve(scopedMappedTFVaryings.size());
905 for (const auto& cur : scopedMappedTFVaryings) {
906 driverVaryings.push_back(cur.c_str());
909 mContext->gl->fTransformFeedbackVaryings(
910 mGLName, driverVaryings.size(), driverVaryings.data(),
911 mNextLink_TransformFeedbackBufferMode);
914 LinkAndUpdate();
916 if (mMostRecentLinkInfo) {
917 std::string postLinkLog;
918 if (ValidateAfterTentativeLink(&postLinkLog)) return;
920 mMostRecentLinkInfo = nullptr;
921 mLinkLog = std::move(postLinkLog);
924 // Failed link.
925 if (mContext->ShouldGenerateWarnings()) {
926 // report shader/program infoLogs as warnings.
927 // note that shader compilation errors can be deferred to linkProgram,
928 // which is why we can't do anything in compileShader. In practice we could
929 // report in compileShader the translation errors generated by ANGLE,
930 // but it seems saner to keep a single way of obtaining shader infologs.
931 if (!mLinkLog.empty()) {
932 mContext->GenerateWarning(
933 "Failed to link, leaving the following"
934 " log:\n%s\n",
935 mLinkLog.c_str());
940 static uint8_t NumUsedLocationsByElemType(GLenum elemType) {
941 // GLES 3.0.4 p55
943 switch (elemType) {
944 case LOCAL_GL_FLOAT_MAT2:
945 case LOCAL_GL_FLOAT_MAT2x3:
946 case LOCAL_GL_FLOAT_MAT2x4:
947 return 2;
949 case LOCAL_GL_FLOAT_MAT3x2:
950 case LOCAL_GL_FLOAT_MAT3:
951 case LOCAL_GL_FLOAT_MAT3x4:
952 return 3;
954 case LOCAL_GL_FLOAT_MAT4x2:
955 case LOCAL_GL_FLOAT_MAT4x3:
956 case LOCAL_GL_FLOAT_MAT4:
957 return 4;
959 default:
960 return 1;
964 uint8_t ElemTypeComponents(const GLenum elemType) {
965 switch (elemType) {
966 case LOCAL_GL_BOOL:
967 case LOCAL_GL_FLOAT:
968 case LOCAL_GL_INT:
969 case LOCAL_GL_UNSIGNED_INT:
970 case LOCAL_GL_SAMPLER_2D:
971 case LOCAL_GL_SAMPLER_3D:
972 case LOCAL_GL_SAMPLER_CUBE:
973 case LOCAL_GL_SAMPLER_2D_SHADOW:
974 case LOCAL_GL_SAMPLER_2D_ARRAY:
975 case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
976 case LOCAL_GL_SAMPLER_CUBE_SHADOW:
977 case LOCAL_GL_INT_SAMPLER_2D:
978 case LOCAL_GL_INT_SAMPLER_3D:
979 case LOCAL_GL_INT_SAMPLER_CUBE:
980 case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
981 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
982 case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
983 case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
984 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
985 return 1;
987 case LOCAL_GL_BOOL_VEC2:
988 case LOCAL_GL_FLOAT_VEC2:
989 case LOCAL_GL_INT_VEC2:
990 case LOCAL_GL_UNSIGNED_INT_VEC2:
991 return 2;
993 case LOCAL_GL_BOOL_VEC3:
994 case LOCAL_GL_FLOAT_VEC3:
995 case LOCAL_GL_INT_VEC3:
996 case LOCAL_GL_UNSIGNED_INT_VEC3:
997 return 3;
999 case LOCAL_GL_BOOL_VEC4:
1000 case LOCAL_GL_FLOAT_VEC4:
1001 case LOCAL_GL_INT_VEC4:
1002 case LOCAL_GL_UNSIGNED_INT_VEC4:
1003 case LOCAL_GL_FLOAT_MAT2:
1004 return 4;
1006 case LOCAL_GL_FLOAT_MAT2x3:
1007 case LOCAL_GL_FLOAT_MAT3x2:
1008 return 2 * 3;
1010 case LOCAL_GL_FLOAT_MAT2x4:
1011 case LOCAL_GL_FLOAT_MAT4x2:
1012 return 2 * 4;
1014 case LOCAL_GL_FLOAT_MAT3:
1015 return 3 * 3;
1017 case LOCAL_GL_FLOAT_MAT3x4:
1018 case LOCAL_GL_FLOAT_MAT4x3:
1019 return 3 * 4;
1021 case LOCAL_GL_FLOAT_MAT4:
1022 return 4 * 4;
1024 default:
1025 return 0;
1029 bool WebGLProgram::ValidateAfterTentativeLink(
1030 std::string* const out_linkLog) const {
1031 const auto& linkInfo = mMostRecentLinkInfo;
1032 const auto& gl = mContext->gl;
1034 // Check for overlapping attrib locations.
1036 std::unordered_map<uint32_t, const std::string&> nameByLoc;
1037 for (const auto& attrib : linkInfo->active.activeAttribs) {
1038 if (attrib.location == -1) continue;
1040 const auto& elemType = attrib.elemType;
1041 const auto numUsedLocs = NumUsedLocationsByElemType(elemType);
1042 for (uint32_t i = 0; i < numUsedLocs; i++) {
1043 const uint32_t usedLoc = attrib.location + i;
1045 const auto res = nameByLoc.insert({usedLoc, attrib.name});
1046 const bool& didInsert = res.second;
1047 if (!didInsert) {
1048 const auto& aliasingName = attrib.name;
1049 const auto& itrExisting = res.first;
1050 const auto& existingName = itrExisting->second;
1051 *out_linkLog = nsPrintfCString(
1052 "Attrib \"%s\" aliases locations used by"
1053 " attrib \"%s\".",
1054 aliasingName.c_str(), existingName.c_str())
1055 .BeginReading();
1056 return false;
1062 // Forbid too many components for specified buffer mode
1063 const auto& activeTfVaryings = linkInfo->active.activeTfVaryings;
1064 MOZ_ASSERT(mNextLink_TransformFeedbackVaryings.size() ==
1065 activeTfVaryings.size());
1066 if (!activeTfVaryings.empty()) {
1067 GLuint maxComponentsPerIndex = 0;
1068 switch (linkInfo->transformFeedbackBufferMode) {
1069 case LOCAL_GL_INTERLEAVED_ATTRIBS:
1070 gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS,
1071 &maxComponentsPerIndex);
1072 break;
1074 case LOCAL_GL_SEPARATE_ATTRIBS:
1075 gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS,
1076 &maxComponentsPerIndex);
1077 break;
1079 default:
1080 MOZ_CRASH("`bufferMode`");
1083 std::vector<size_t> componentsPerVert;
1084 for (const auto& cur : activeTfVaryings) {
1085 if (componentsPerVert.empty() ||
1086 linkInfo->transformFeedbackBufferMode == LOCAL_GL_SEPARATE_ATTRIBS) {
1087 componentsPerVert.push_back(0);
1090 size_t varyingComponents = ElemTypeComponents(cur.elemType);
1091 MOZ_ASSERT(varyingComponents);
1092 varyingComponents *= cur.elemCount;
1094 auto& totalComponentsForIndex = *(componentsPerVert.rbegin());
1095 totalComponentsForIndex += varyingComponents;
1097 if (totalComponentsForIndex > maxComponentsPerIndex) {
1098 *out_linkLog = nsPrintfCString(
1099 "Transform feedback varying \"%s\""
1100 " pushed `componentsForIndex` over the"
1101 " limit of %u.",
1102 cur.name.c_str(), maxComponentsPerIndex)
1103 .BeginReading();
1104 return false;
1108 linkInfo->componentsPerTFVert = std::move(componentsPerVert);
1111 return true;
1114 bool WebGLProgram::UseProgram() const {
1115 if (!mMostRecentLinkInfo) {
1116 mContext->ErrorInvalidOperation(
1117 "Program has not been successfully linked.");
1118 return false;
1121 if (mContext->mBoundTransformFeedback &&
1122 mContext->mBoundTransformFeedback->mIsActive &&
1123 !mContext->mBoundTransformFeedback->mIsPaused) {
1124 mContext->ErrorInvalidOperation(
1125 "Transform feedback active and not paused.");
1126 return false;
1129 mContext->gl->fUseProgram(mGLName);
1130 return true;
1133 bool WebGLProgram::ValidateProgram() const {
1134 gl::GLContext* gl = mContext->gl;
1136 #ifdef XP_MACOSX
1137 // See bug 593867 for NVIDIA and bug 657201 for ATI. The latter is confirmed
1138 // with Mac OS 10.6.7.
1139 if (gl->WorkAroundDriverBugs()) {
1140 mContext->GenerateWarning(
1141 "Implemented as a no-op on"
1142 " Mac to work around crashes.");
1143 return true;
1145 #endif
1147 gl->fValidateProgram(mGLName);
1148 GLint ok = 0;
1149 gl->fGetProgramiv(mGLName, LOCAL_GL_VALIDATE_STATUS, &ok);
1150 return bool(ok);
1153 ////////////////////////////////////////////////////////////////////////////////
1155 void WebGLProgram::LinkAndUpdate() {
1156 mMostRecentLinkInfo = nullptr;
1158 gl::GLContext* gl = mContext->gl;
1159 gl->fLinkProgram(mGLName);
1161 // Grab the program log.
1163 GLuint logLenWithNull = 0;
1164 gl->fGetProgramiv(mGLName, LOCAL_GL_INFO_LOG_LENGTH,
1165 (GLint*)&logLenWithNull);
1166 if (logLenWithNull > 1) {
1167 std::vector<char> buffer(logLenWithNull);
1168 gl->fGetProgramInfoLog(mGLName, buffer.size(), nullptr, buffer.data());
1169 mLinkLog = buffer.data();
1170 } else {
1171 mLinkLog.clear();
1175 GLint ok = 0;
1176 gl->fGetProgramiv(mGLName, LOCAL_GL_LINK_STATUS, &ok);
1177 if (!ok) return;
1179 mMostRecentLinkInfo =
1180 QueryProgramInfo(this, gl); // Fallible after context loss.
1183 void WebGLProgram::TransformFeedbackVaryings(
1184 const std::vector<std::string>& varyings, GLenum bufferMode) {
1185 const auto& gl = mContext->gl;
1187 switch (bufferMode) {
1188 case LOCAL_GL_INTERLEAVED_ATTRIBS:
1189 break;
1191 case LOCAL_GL_SEPARATE_ATTRIBS: {
1192 GLuint maxAttribs = 0;
1193 gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS,
1194 &maxAttribs);
1195 if (varyings.size() > maxAttribs) {
1196 mContext->ErrorInvalidValue("Length of `varyings` exceeds %s.",
1197 "TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS");
1198 return;
1200 } break;
1202 default:
1203 mContext->ErrorInvalidEnumInfo("bufferMode", bufferMode);
1204 return;
1207 ////
1209 mNextLink_TransformFeedbackVaryings = varyings;
1210 mNextLink_TransformFeedbackBufferMode = bufferMode;
1213 } // namespace mozilla