Bug 1936278 - Prevent search mode chiclet from being dismissed when clicking in page...
[gecko.git] / dom / canvas / WebGLProgram.cpp
blob6188344f3b7382ba617a4d08d45419a088d27a46
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 "WebGLFormats.h"
18 #include "WebGLShader.h"
19 #include "WebGLShaderValidator.h"
20 #include "WebGLTransformFeedback.h"
21 #include "WebGLValidateStrings.h"
22 #include "WebGLVertexArray.h"
24 namespace mozilla {
26 static bool IsShadowSampler(const GLenum elemType) {
27 switch (elemType) {
28 case LOCAL_GL_SAMPLER_2D_SHADOW:
29 case LOCAL_GL_SAMPLER_CUBE_SHADOW:
30 case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
31 return true;
32 default:
33 return false;
37 static Maybe<webgl::TextureBaseType> SamplerBaseType(const GLenum elemType) {
38 switch (elemType) {
39 case LOCAL_GL_SAMPLER_2D:
40 case LOCAL_GL_SAMPLER_3D:
41 case LOCAL_GL_SAMPLER_CUBE:
42 case LOCAL_GL_SAMPLER_2D_ARRAY:
43 case LOCAL_GL_SAMPLER_2D_SHADOW:
44 case LOCAL_GL_SAMPLER_CUBE_SHADOW:
45 case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
46 return Some(webgl::TextureBaseType::Float);
48 case LOCAL_GL_INT_SAMPLER_2D:
49 case LOCAL_GL_INT_SAMPLER_3D:
50 case LOCAL_GL_INT_SAMPLER_CUBE:
51 case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
52 return Some(webgl::TextureBaseType::Int);
54 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
55 case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
56 case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
57 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
58 return Some(webgl::TextureBaseType::UInt);
60 default:
61 return {};
65 //////////
67 static webgl::TextureBaseType FragOutputBaseType(const GLenum type) {
68 switch (type) {
69 case LOCAL_GL_FLOAT:
70 case LOCAL_GL_FLOAT_VEC2:
71 case LOCAL_GL_FLOAT_VEC3:
72 case LOCAL_GL_FLOAT_VEC4:
73 return webgl::TextureBaseType::Float;
75 case LOCAL_GL_INT:
76 case LOCAL_GL_INT_VEC2:
77 case LOCAL_GL_INT_VEC3:
78 case LOCAL_GL_INT_VEC4:
79 return webgl::TextureBaseType::Int;
81 case LOCAL_GL_UNSIGNED_INT:
82 case LOCAL_GL_UNSIGNED_INT_VEC2:
83 case LOCAL_GL_UNSIGNED_INT_VEC3:
84 case LOCAL_GL_UNSIGNED_INT_VEC4:
85 return webgl::TextureBaseType::UInt;
87 default:
88 break;
91 const auto& str = EnumString(type);
92 gfxCriticalError() << "Unhandled enum for FragOutputBaseType: "
93 << str.c_str();
94 return webgl::TextureBaseType::Float;
97 // -----------------------------------------
99 namespace webgl {
101 void UniformAs1fv(gl::GLContext& gl, GLint location, GLsizei count,
102 bool transpose, const void* any) {
103 gl.fUniform1fv(location, count, static_cast<const float*>(any));
105 void UniformAs2fv(gl::GLContext& gl, GLint location, GLsizei count,
106 bool transpose, const void* any) {
107 gl.fUniform2fv(location, count, static_cast<const float*>(any));
109 void UniformAs3fv(gl::GLContext& gl, GLint location, GLsizei count,
110 bool transpose, const void* any) {
111 gl.fUniform3fv(location, count, static_cast<const float*>(any));
113 void UniformAs4fv(gl::GLContext& gl, GLint location, GLsizei count,
114 bool transpose, const void* any) {
115 gl.fUniform4fv(location, count, static_cast<const float*>(any));
118 void UniformAs1iv(gl::GLContext& gl, GLint location, GLsizei count,
119 bool transpose, const void* any) {
120 gl.fUniform1iv(location, count, static_cast<const int32_t*>(any));
122 void UniformAs2iv(gl::GLContext& gl, GLint location, GLsizei count,
123 bool transpose, const void* any) {
124 gl.fUniform2iv(location, count, static_cast<const int32_t*>(any));
126 void UniformAs3iv(gl::GLContext& gl, GLint location, GLsizei count,
127 bool transpose, const void* any) {
128 gl.fUniform3iv(location, count, static_cast<const int32_t*>(any));
130 void UniformAs4iv(gl::GLContext& gl, GLint location, GLsizei count,
131 bool transpose, const void* any) {
132 gl.fUniform4iv(location, count, static_cast<const int32_t*>(any));
135 void UniformAs1uiv(gl::GLContext& gl, GLint location, GLsizei count,
136 bool transpose, const void* any) {
137 gl.fUniform1uiv(location, count, static_cast<const uint32_t*>(any));
139 void UniformAs2uiv(gl::GLContext& gl, GLint location, GLsizei count,
140 bool transpose, const void* any) {
141 gl.fUniform2uiv(location, count, static_cast<const uint32_t*>(any));
143 void UniformAs3uiv(gl::GLContext& gl, GLint location, GLsizei count,
144 bool transpose, const void* any) {
145 gl.fUniform3uiv(location, count, static_cast<const uint32_t*>(any));
147 void UniformAs4uiv(gl::GLContext& gl, GLint location, GLsizei count,
148 bool transpose, const void* any) {
149 gl.fUniform4uiv(location, count, static_cast<const uint32_t*>(any));
152 void UniformAsMatrix2x2fv(gl::GLContext& gl, GLint location, GLsizei count,
153 bool transpose, const void* any) {
154 gl.fUniformMatrix2fv(location, count, transpose,
155 static_cast<const float*>(any));
157 void UniformAsMatrix2x3fv(gl::GLContext& gl, GLint location, GLsizei count,
158 bool transpose, const void* any) {
159 gl.fUniformMatrix2x3fv(location, count, transpose,
160 static_cast<const float*>(any));
162 void UniformAsMatrix2x4fv(gl::GLContext& gl, GLint location, GLsizei count,
163 bool transpose, const void* any) {
164 gl.fUniformMatrix2x4fv(location, count, transpose,
165 static_cast<const float*>(any));
168 void UniformAsMatrix3x2fv(gl::GLContext& gl, GLint location, GLsizei count,
169 bool transpose, const void* any) {
170 gl.fUniformMatrix3x2fv(location, count, transpose,
171 static_cast<const float*>(any));
173 void UniformAsMatrix3x3fv(gl::GLContext& gl, GLint location, GLsizei count,
174 bool transpose, const void* any) {
175 gl.fUniformMatrix3fv(location, count, transpose,
176 static_cast<const float*>(any));
178 void UniformAsMatrix3x4fv(gl::GLContext& gl, GLint location, GLsizei count,
179 bool transpose, const void* any) {
180 gl.fUniformMatrix3x4fv(location, count, transpose,
181 static_cast<const float*>(any));
184 void UniformAsMatrix4x2fv(gl::GLContext& gl, GLint location, GLsizei count,
185 bool transpose, const void* any) {
186 gl.fUniformMatrix4x2fv(location, count, transpose,
187 static_cast<const float*>(any));
189 void UniformAsMatrix4x3fv(gl::GLContext& gl, GLint location, GLsizei count,
190 bool transpose, const void* any) {
191 gl.fUniformMatrix4x3fv(location, count, transpose,
192 static_cast<const float*>(any));
194 void UniformAsMatrix4x4fv(gl::GLContext& gl, GLint location, GLsizei count,
195 bool transpose, const void* any) {
196 gl.fUniformMatrix4fv(location, count, transpose,
197 static_cast<const float*>(any));
200 // -
202 static bool EndsWith(const std::string& str, const std::string& needle) {
203 if (str.length() < needle.length()) return false;
204 return str.compare(str.length() - needle.length(), needle.length(), needle) ==
208 webgl::ActiveUniformValidationInfo webgl::ActiveUniformValidationInfo::Make(
209 const webgl::ActiveUniformInfo& info) {
210 auto ret = webgl::ActiveUniformValidationInfo{info};
211 ret.isArray = EndsWith(info.name, "[0]");
213 switch (info.elemType) {
214 case LOCAL_GL_FLOAT:
215 ret.channelsPerElem = 1;
216 ret.pfn = &UniformAs1fv;
217 break;
218 case LOCAL_GL_FLOAT_VEC2:
219 ret.channelsPerElem = 2;
220 ret.pfn = &UniformAs2fv;
221 break;
222 case LOCAL_GL_FLOAT_VEC3:
223 ret.channelsPerElem = 3;
224 ret.pfn = &UniformAs3fv;
225 break;
226 case LOCAL_GL_FLOAT_VEC4:
227 ret.channelsPerElem = 4;
228 ret.pfn = &UniformAs4fv;
229 break;
231 case LOCAL_GL_SAMPLER_2D:
232 case LOCAL_GL_SAMPLER_3D:
233 case LOCAL_GL_SAMPLER_CUBE:
234 case LOCAL_GL_SAMPLER_2D_SHADOW:
235 case LOCAL_GL_SAMPLER_2D_ARRAY:
236 case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
237 case LOCAL_GL_SAMPLER_CUBE_SHADOW:
238 case LOCAL_GL_INT_SAMPLER_2D:
239 case LOCAL_GL_INT_SAMPLER_3D:
240 case LOCAL_GL_INT_SAMPLER_CUBE:
241 case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
242 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
243 case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
244 case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
245 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
246 case LOCAL_GL_BOOL:
247 case LOCAL_GL_INT:
248 ret.channelsPerElem = 1;
249 ret.pfn = &UniformAs1iv;
250 break;
251 case LOCAL_GL_BOOL_VEC2:
252 case LOCAL_GL_INT_VEC2:
253 ret.channelsPerElem = 2;
254 ret.pfn = &UniformAs2iv;
255 break;
256 case LOCAL_GL_BOOL_VEC3:
257 case LOCAL_GL_INT_VEC3:
258 ret.channelsPerElem = 3;
259 ret.pfn = &UniformAs3iv;
260 break;
261 case LOCAL_GL_BOOL_VEC4:
262 case LOCAL_GL_INT_VEC4:
263 ret.channelsPerElem = 4;
264 ret.pfn = &UniformAs4iv;
265 break;
267 case LOCAL_GL_UNSIGNED_INT:
268 ret.channelsPerElem = 1;
269 ret.pfn = &UniformAs1uiv;
270 break;
271 case LOCAL_GL_UNSIGNED_INT_VEC2:
272 ret.channelsPerElem = 2;
273 ret.pfn = &UniformAs2uiv;
274 break;
275 case LOCAL_GL_UNSIGNED_INT_VEC3:
276 ret.channelsPerElem = 3;
277 ret.pfn = &UniformAs3uiv;
278 break;
279 case LOCAL_GL_UNSIGNED_INT_VEC4:
280 ret.channelsPerElem = 4;
281 ret.pfn = &UniformAs4uiv;
282 break;
284 // -
286 case LOCAL_GL_FLOAT_MAT2:
287 ret.channelsPerElem = 2 * 2;
288 ret.pfn = &UniformAsMatrix2x2fv;
289 break;
290 case LOCAL_GL_FLOAT_MAT2x3:
291 ret.channelsPerElem = 2 * 3;
292 ret.pfn = &UniformAsMatrix2x3fv;
293 break;
294 case LOCAL_GL_FLOAT_MAT2x4:
295 ret.channelsPerElem = 2 * 4;
296 ret.pfn = &UniformAsMatrix2x4fv;
297 break;
299 case LOCAL_GL_FLOAT_MAT3x2:
300 ret.channelsPerElem = 3 * 2;
301 ret.pfn = &UniformAsMatrix3x2fv;
302 break;
303 case LOCAL_GL_FLOAT_MAT3:
304 ret.channelsPerElem = 3 * 3;
305 ret.pfn = &UniformAsMatrix3x3fv;
306 break;
307 case LOCAL_GL_FLOAT_MAT3x4:
308 ret.channelsPerElem = 3 * 4;
309 ret.pfn = &UniformAsMatrix3x4fv;
310 break;
312 case LOCAL_GL_FLOAT_MAT4x2:
313 ret.channelsPerElem = 4 * 2;
314 ret.pfn = &UniformAsMatrix4x2fv;
315 break;
316 case LOCAL_GL_FLOAT_MAT4x3:
317 ret.channelsPerElem = 4 * 3;
318 ret.pfn = &UniformAsMatrix4x3fv;
319 break;
320 case LOCAL_GL_FLOAT_MAT4:
321 ret.channelsPerElem = 4 * 4;
322 ret.pfn = &UniformAsMatrix4x4fv;
323 break;
325 default:
326 gfxCriticalError() << "Bad `elemType`: " << EnumString(info.elemType);
327 MOZ_CRASH("`elemType`");
329 return ret;
332 } // namespace webgl
334 // -------------------------
336 // #define DUMP_SHADERVAR_MAPPINGS
338 RefPtr<const webgl::LinkedProgramInfo> QueryProgramInfo(WebGLProgram* prog,
339 gl::GLContext* gl) {
340 WebGLContext* const webgl = prog->mContext;
342 RefPtr<webgl::LinkedProgramInfo> info(new webgl::LinkedProgramInfo(prog));
344 // Frag outputs
347 const auto& fragShader = prog->FragShader();
348 const auto& compileResults = fragShader->CompileResults();
349 const auto version = compileResults->mShaderVersion;
351 const auto fnAddInfo = [&](const webgl::FragOutputInfo& x) {
352 info->hasOutput[x.loc] = true;
353 info->fragOutputs.insert({x.loc, x});
356 if (version == 300) {
357 for (const auto& cur : compileResults->mOutputVariables) {
358 auto loc = cur.location;
359 if (loc == -1) loc = 0;
361 const auto info =
362 webgl::FragOutputInfo{uint8_t(loc), cur.name, cur.mappedName,
363 FragOutputBaseType(cur.type)};
364 if (!cur.isArray()) {
365 fnAddInfo(info);
366 continue;
368 MOZ_ASSERT(cur.arraySizes.size() == 1);
369 for (uint32_t i = 0; i < cur.arraySizes[0]; ++i) {
370 const auto indexStr = std::string("[") + std::to_string(i) + "]";
372 const auto userName = info.userName + indexStr;
373 const auto mappedName = info.mappedName + indexStr;
375 const auto indexedInfo = webgl::FragOutputInfo{
376 uint8_t(info.loc + i), userName, mappedName, info.baseType};
377 fnAddInfo(indexedInfo);
380 } else {
381 // ANGLE's translator doesn't tell us about non-user frag outputs. :(
383 const auto& translatedSource = compileResults->mObjectCode;
384 uint32_t drawBuffers = 1;
385 if (translatedSource.find("(gl_FragData[1]") != std::string::npos ||
386 translatedSource.find("(webgl_FragData[1]") != std::string::npos) {
387 // The matching with the leading '(' prevents cleverly-named user vars
388 // breaking this. Since ANGLE initializes all outputs, if this is an MRT
389 // shader, FragData[1] will be present. FragData[0] is valid for non-MRT
390 // shaders.
391 drawBuffers = webgl->GLMaxDrawBuffers();
392 } else if (translatedSource.find("(gl_FragColor") == std::string::npos &&
393 translatedSource.find("(webgl_FragColor") ==
394 std::string::npos &&
395 translatedSource.find("(gl_FragData") == std::string::npos &&
396 translatedSource.find("(webgl_FragData") ==
397 std::string::npos) {
398 // We have to support no-color-output shaders?
399 drawBuffers = 0;
402 for (uint32_t i = 0; i < drawBuffers; ++i) {
403 const auto name = std::string("gl_FragData[") + std::to_string(i) + "]";
404 const auto info = webgl::FragOutputInfo{uint8_t(i), name, name,
405 webgl::TextureBaseType::Float};
406 fnAddInfo(info);
411 const auto& vertShader = prog->VertShader();
412 const auto& vertCompileResults = vertShader->CompileResults();
413 const auto numViews = vertCompileResults->mVertexShaderNumViews;
414 if (numViews != -1) {
415 info->zLayerCount = AssertedCast<uint8_t>(numViews);
418 // -
420 auto& nameMap = info->nameMap;
422 const auto fnAccum = [&](WebGLShader& shader) {
423 const auto& compRes = shader.CompileResults();
424 for (const auto& pair : compRes->mNameMap) {
425 nameMap.insert(pair);
428 fnAccum(*prog->VertShader());
429 fnAccum(*prog->FragShader());
431 // -
433 std::unordered_map<std::string, std::string> nameUnmap;
434 for (const auto& pair : nameMap) {
435 nameUnmap.insert({pair.second, pair.first});
438 info->active =
439 GetLinkActiveInfo(*gl, prog->mGLName, webgl->IsWebGL2(), nameUnmap);
441 // -
443 for (const auto& attrib : info->active.activeAttribs) {
444 if (attrib.location == 0) {
445 info->attrib0Active = true;
446 break;
450 info->webgl_gl_VertexID_Offset =
451 gl->fGetUniformLocation(prog->mGLName, "webgl_gl_VertexID_Offset");
453 // -
455 for (const auto& uniform : info->active.activeUniforms) {
456 const auto& elemType = uniform.elemType;
457 webgl::SamplerUniformInfo* samplerInfo = nullptr;
458 const auto baseType = SamplerBaseType(elemType);
459 if (baseType) {
460 const bool isShadowSampler = IsShadowSampler(elemType);
462 auto* texList = &webgl->mBound2DTextures;
464 switch (elemType) {
465 case LOCAL_GL_SAMPLER_2D:
466 case LOCAL_GL_SAMPLER_2D_SHADOW:
467 case LOCAL_GL_INT_SAMPLER_2D:
468 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
469 break;
471 case LOCAL_GL_SAMPLER_CUBE:
472 case LOCAL_GL_SAMPLER_CUBE_SHADOW:
473 case LOCAL_GL_INT_SAMPLER_CUBE:
474 case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
475 texList = &webgl->mBoundCubeMapTextures;
476 break;
478 case LOCAL_GL_SAMPLER_3D:
479 case LOCAL_GL_INT_SAMPLER_3D:
480 case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
481 texList = &webgl->mBound3DTextures;
482 break;
484 case LOCAL_GL_SAMPLER_2D_ARRAY:
485 case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
486 case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
487 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
488 texList = &webgl->mBound2DArrayTextures;
489 break;
492 auto curInfo = std::unique_ptr<webgl::SamplerUniformInfo>(
493 new webgl::SamplerUniformInfo{*texList, *baseType, isShadowSampler});
494 MOZ_RELEASE_ASSERT(curInfo->texUnits.resize(uniform.elemCount));
495 samplerInfo = curInfo.get();
496 info->samplerUniforms.push_back(std::move(curInfo));
499 const auto valInfo = webgl::ActiveUniformValidationInfo::Make(uniform);
501 for (const auto& pair : uniform.locByIndex) {
502 info->locationMap.insert(
503 {pair.second, {valInfo, pair.first, samplerInfo}});
507 // -
510 const auto& activeBlocks = info->active.activeUniformBlocks;
511 info->uniformBlocks.reserve(activeBlocks.size());
512 for (const auto& cur : activeBlocks) {
513 const auto curInfo = webgl::UniformBlockInfo{
514 cur, &webgl->mIndexedUniformBufferBindings[0]};
515 info->uniformBlocks.push_back(curInfo);
519 return info;
522 ////////////////////////////////////////////////////////////////////////////////
524 webgl::LinkedProgramInfo::LinkedProgramInfo(WebGLProgram* prog)
525 : prog(prog),
526 transformFeedbackBufferMode(prog->mNextLink_TransformFeedbackBufferMode) {
529 webgl::LinkedProgramInfo::~LinkedProgramInfo() = default;
531 webgl::AttribBaseType webgl::ToAttribBaseType(const GLenum elemType) {
532 switch (elemType) {
533 case LOCAL_GL_BOOL:
534 case LOCAL_GL_BOOL_VEC2:
535 case LOCAL_GL_BOOL_VEC3:
536 case LOCAL_GL_BOOL_VEC4:
537 return webgl::AttribBaseType::Boolean;
539 case LOCAL_GL_FLOAT:
540 case LOCAL_GL_FLOAT_VEC2:
541 case LOCAL_GL_FLOAT_VEC3:
542 case LOCAL_GL_FLOAT_VEC4:
543 case LOCAL_GL_FLOAT_MAT2:
544 case LOCAL_GL_FLOAT_MAT2x3:
545 case LOCAL_GL_FLOAT_MAT3x2:
546 case LOCAL_GL_FLOAT_MAT2x4:
547 case LOCAL_GL_FLOAT_MAT4x2:
548 case LOCAL_GL_FLOAT_MAT3:
549 case LOCAL_GL_FLOAT_MAT3x4:
550 case LOCAL_GL_FLOAT_MAT4x3:
551 case LOCAL_GL_FLOAT_MAT4:
552 return webgl::AttribBaseType::Float;
554 case LOCAL_GL_INT:
555 case LOCAL_GL_INT_VEC2:
556 case LOCAL_GL_INT_VEC3:
557 case LOCAL_GL_INT_VEC4:
558 case LOCAL_GL_SAMPLER_2D:
559 case LOCAL_GL_SAMPLER_3D:
560 case LOCAL_GL_SAMPLER_CUBE:
561 case LOCAL_GL_SAMPLER_2D_SHADOW:
562 case LOCAL_GL_SAMPLER_2D_ARRAY:
563 case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
564 case LOCAL_GL_SAMPLER_CUBE_SHADOW:
565 case LOCAL_GL_INT_SAMPLER_2D:
566 case LOCAL_GL_INT_SAMPLER_3D:
567 case LOCAL_GL_INT_SAMPLER_CUBE:
568 case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
569 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
570 case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
571 case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
572 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
573 return webgl::AttribBaseType::Int;
575 case LOCAL_GL_UNSIGNED_INT:
576 case LOCAL_GL_UNSIGNED_INT_VEC2:
577 case LOCAL_GL_UNSIGNED_INT_VEC3:
578 case LOCAL_GL_UNSIGNED_INT_VEC4:
579 return webgl::AttribBaseType::Uint;
581 default:
582 gfxCriticalError() << "Bad `elemType`: " << EnumString(elemType);
583 MOZ_CRASH("`elemType`");
587 const char* webgl::ToString(const webgl::AttribBaseType x) {
588 switch (x) {
589 case webgl::AttribBaseType::Float:
590 return "FLOAT";
591 case webgl::AttribBaseType::Int:
592 return "INT";
593 case webgl::AttribBaseType::Uint:
594 return "UINT";
595 case webgl::AttribBaseType::Boolean:
596 return "BOOL";
598 MOZ_CRASH("pacify gcc6 warning");
601 const char* webgl::ToString(const webgl::UniformBaseType x) {
602 switch (x) {
603 case webgl::UniformBaseType::Float:
604 return "FLOAT";
605 case webgl::UniformBaseType::Int:
606 return "INT";
607 case webgl::UniformBaseType::Uint:
608 return "UINT";
610 MOZ_CRASH("pacify gcc6 warning");
613 const webgl::CachedDrawFetchLimits*
614 webgl::LinkedProgramInfo::GetDrawFetchLimits() const {
615 const auto& webgl = prog->mContext;
616 const auto& vao = webgl->mBoundVertexArray;
619 // We have to ensure that every enabled attrib array (not just the active
620 // ones) has a non-null buffer.
621 const auto badIndex = vao->GetAttribIsArrayWithNullBuffer();
622 if (badIndex) {
623 webgl->ErrorInvalidOperation(
624 "Vertex attrib array %u is enabled but"
625 " has no buffer bound.",
626 *badIndex);
627 return nullptr;
631 const auto& activeAttribs = active.activeAttribs;
633 webgl::CachedDrawFetchLimits fetchLimits;
634 fetchLimits.usedBuffers =
635 std::move(mScratchFetchLimits.usedBuffers); // Avoid realloc.
636 fetchLimits.usedBuffers.clear();
637 fetchLimits.usedBuffers.reserve(activeAttribs.size());
639 bool hasActiveAttrib = false;
640 bool hasActiveDivisor0 = false;
642 for (const auto& progAttrib : activeAttribs) {
643 const auto& loc = progAttrib.location;
644 if (loc == -1) continue;
645 hasActiveAttrib |= true;
647 const auto& binding = vao->AttribBinding(loc);
648 const auto& buffer = binding.buffer;
649 const auto& layout = binding.layout;
650 hasActiveDivisor0 |= (layout.divisor == 0);
652 webgl::AttribBaseType attribDataBaseType;
653 if (layout.isArray) {
654 MOZ_ASSERT(buffer);
655 fetchLimits.usedBuffers.push_back(
656 {buffer.get(), static_cast<uint32_t>(loc)});
658 attribDataBaseType = layout.baseType;
660 const auto availBytes = buffer->ByteLength();
661 const auto availElems = AvailGroups(availBytes, layout.byteOffset,
662 layout.byteSize, layout.byteStride);
663 if (layout.divisor) {
664 const auto availInstances =
665 CheckedInt<uint64_t>(availElems) * layout.divisor;
666 if (availInstances.isValid()) {
667 fetchLimits.maxInstances =
668 std::min(fetchLimits.maxInstances, availInstances.value());
669 } // If not valid, it overflowed too large, so we're super safe.
670 } else {
671 fetchLimits.maxVerts = std::min(fetchLimits.maxVerts, availElems);
673 } else {
674 attribDataBaseType = webgl->mGenericVertexAttribTypes[loc];
677 const auto& progBaseType = progAttrib.baseType;
678 if ((attribDataBaseType != progBaseType) &&
679 (progBaseType != webgl::AttribBaseType::Boolean)) {
680 const auto& dataType = ToString(attribDataBaseType);
681 const auto& progType = ToString(progBaseType);
682 webgl->ErrorInvalidOperation(
683 "Vertex attrib %u requires data of type %s,"
684 " but is being supplied with type %s.",
685 loc, progType, dataType);
686 return nullptr;
690 if (!webgl->IsWebGL2() && hasActiveAttrib && !hasActiveDivisor0) {
691 webgl->ErrorInvalidOperation(
692 "One active vertex attrib (if any are active)"
693 " must have a divisor of 0.");
694 return nullptr;
697 // -
699 mScratchFetchLimits = std::move(fetchLimits);
700 return &mScratchFetchLimits;
703 ////////////////////////////////////////////////////////////////////////////////
704 // WebGLProgram
706 WebGLProgram::WebGLProgram(WebGLContext* webgl)
707 : WebGLContextBoundObject(webgl),
708 mGLName(webgl->gl->fCreateProgram()),
709 mNumActiveTFOs(0),
710 mNextLink_TransformFeedbackBufferMode(LOCAL_GL_INTERLEAVED_ATTRIBS) {}
712 WebGLProgram::~WebGLProgram() {
713 mVertShader = nullptr;
714 mFragShader = nullptr;
716 mMostRecentLinkInfo = nullptr;
718 if (!mContext) return;
719 mContext->gl->fDeleteProgram(mGLName);
722 ////////////////////////////////////////////////////////////////////////////////
723 // GL funcs
725 void WebGLProgram::AttachShader(WebGLShader& shader) {
726 RefPtr<WebGLShader>* shaderSlot = nullptr;
727 switch (shader.mType) {
728 case LOCAL_GL_VERTEX_SHADER:
729 shaderSlot = &mVertShader;
730 break;
731 case LOCAL_GL_FRAGMENT_SHADER:
732 shaderSlot = &mFragShader;
733 break;
735 MOZ_ASSERT(shaderSlot);
737 *shaderSlot = &shader;
739 mContext->gl->fAttachShader(mGLName, shader.mGLName);
742 void WebGLProgram::BindAttribLocation(GLuint loc, const std::string& name) {
743 const auto err = CheckGLSLVariableName(mContext->IsWebGL2(), name);
744 if (err) {
745 mContext->GenerateError(err->type, "%s", err->info.c_str());
746 return;
749 if (loc >= mContext->MaxVertexAttribs()) {
750 mContext->ErrorInvalidValue(
751 "`location` must be less than"
752 " MAX_VERTEX_ATTRIBS.");
753 return;
756 if (name.find("gl_") == 0) {
757 mContext->ErrorInvalidOperation(
758 "Can't set the location of a"
759 " name that starts with 'gl_'.");
760 return;
763 auto res = mNextLink_BoundAttribLocs.insert({name, loc});
765 const auto& wasInserted = res.second;
766 if (!wasInserted) {
767 const auto& itr = res.first;
768 itr->second = loc;
772 void WebGLProgram::DetachShader(const WebGLShader& shader) {
773 RefPtr<WebGLShader>* shaderSlot = nullptr;
774 switch (shader.mType) {
775 case LOCAL_GL_VERTEX_SHADER:
776 shaderSlot = &mVertShader;
777 break;
778 case LOCAL_GL_FRAGMENT_SHADER:
779 shaderSlot = &mFragShader;
780 break;
782 MOZ_ASSERT(shaderSlot);
784 if (*shaderSlot != &shader) return;
786 *shaderSlot = nullptr;
788 mContext->gl->fDetachShader(mGLName, shader.mGLName);
791 void WebGLProgram::UniformBlockBinding(GLuint uniformBlockIndex,
792 GLuint uniformBlockBinding) const {
793 if (!IsLinked()) {
794 mContext->ErrorInvalidOperation("`program` must be linked.");
795 return;
798 auto& uniformBlocks = LinkInfo()->uniformBlocks;
799 if (uniformBlockIndex >= uniformBlocks.size()) {
800 mContext->ErrorInvalidValue("Index %u invalid.", uniformBlockIndex);
801 return;
803 auto& uniformBlock = uniformBlocks[uniformBlockIndex];
805 const auto& indexedBindings = mContext->mIndexedUniformBufferBindings;
806 if (uniformBlockBinding >= indexedBindings.size()) {
807 mContext->ErrorInvalidValue("Binding %u invalid.", uniformBlockBinding);
808 return;
810 const auto& indexedBinding = indexedBindings[uniformBlockBinding];
812 ////
814 gl::GLContext* gl = mContext->GL();
815 gl->fUniformBlockBinding(mGLName, uniformBlockIndex, uniformBlockBinding);
817 ////
819 uniformBlock.binding = &indexedBinding;
822 bool WebGLProgram::ValidateForLink() {
823 const auto AppendCompileLog = [&](const WebGLShader* const shader) {
824 if (!shader) {
825 mLinkLog += " Missing shader.";
826 return;
828 mLinkLog += "\nSHADER_INFO_LOG:\n";
829 mLinkLog += shader->CompileLog();
832 if (!mVertShader || !mVertShader->IsCompiled()) {
833 mLinkLog = "Must have a compiled vertex shader attached:";
834 AppendCompileLog(mVertShader);
835 return false;
837 const auto& vertInfo = *mVertShader->CompileResults();
839 if (!mFragShader || !mFragShader->IsCompiled()) {
840 mLinkLog = "Must have a compiled fragment shader attached:";
841 AppendCompileLog(mFragShader);
842 return false;
844 const auto& fragInfo = *mFragShader->CompileResults();
846 nsCString errInfo;
847 if (!fragInfo.CanLinkTo(vertInfo, &errInfo)) {
848 mLinkLog = errInfo.BeginReading();
849 return false;
852 const auto& gl = mContext->gl;
854 if (gl->WorkAroundDriverBugs() && mContext->mIsMesa) {
855 // Bug 1203135: Mesa crashes internally if we exceed the reported maximum
856 // attribute count.
857 if (mVertShader->NumAttributes() > mContext->MaxVertexAttribs()) {
858 mLinkLog =
859 "Number of attributes exceeds Mesa's reported max"
860 " attribute count.";
861 return false;
865 return true;
868 void WebGLProgram::LinkProgram() {
869 if (mNumActiveTFOs) {
870 mContext->ErrorInvalidOperation(
871 "Program is in-use by one or more active"
872 " transform feedback objects.");
873 return;
876 // as some of the validation changes program state
878 mLinkLog = {};
879 mMostRecentLinkInfo = nullptr;
881 if (!ValidateForLink()) {
882 mContext->GenerateWarning("%s", mLinkLog.c_str());
883 return;
886 // Bind the attrib locations.
887 // This can't be done trivially, because we have to deal with mapped attrib
888 // names.
889 for (const auto& pair : mNextLink_BoundAttribLocs) {
890 const auto& name = pair.first;
891 const auto& index = pair.second;
893 mVertShader->BindAttribLocation(mGLName, name, index);
896 // Storage for transform feedback varyings before link.
897 // (Work around for bug seen on nVidia drivers.)
898 std::vector<std::string> scopedMappedTFVaryings;
900 if (mContext->IsWebGL2()) {
901 mVertShader->MapTransformFeedbackVaryings(
902 mNextLink_TransformFeedbackVaryings, &scopedMappedTFVaryings);
904 std::vector<const char*> driverVaryings;
905 driverVaryings.reserve(scopedMappedTFVaryings.size());
906 for (const auto& cur : scopedMappedTFVaryings) {
907 driverVaryings.push_back(cur.c_str());
910 mContext->gl->fTransformFeedbackVaryings(
911 mGLName, driverVaryings.size(), driverVaryings.data(),
912 mNextLink_TransformFeedbackBufferMode);
915 LinkAndUpdate();
917 if (mMostRecentLinkInfo) {
918 std::string postLinkLog;
919 if (ValidateAfterTentativeLink(&postLinkLog)) return;
921 mMostRecentLinkInfo = nullptr;
922 mLinkLog = std::move(postLinkLog);
925 // Failed link.
926 if (mContext->ShouldGenerateWarnings()) {
927 // report shader/program infoLogs as warnings.
928 // note that shader compilation errors can be deferred to linkProgram,
929 // which is why we can't do anything in compileShader. In practice we could
930 // report in compileShader the translation errors generated by ANGLE,
931 // but it seems saner to keep a single way of obtaining shader infologs.
932 if (!mLinkLog.empty()) {
933 mContext->GenerateWarning(
934 "Failed to link, leaving the following"
935 " log:\n%s\n",
936 mLinkLog.c_str());
941 static uint8_t NumUsedLocationsByElemType(GLenum elemType) {
942 // GLES 3.0.4 p55
944 switch (elemType) {
945 case LOCAL_GL_FLOAT_MAT2:
946 case LOCAL_GL_FLOAT_MAT2x3:
947 case LOCAL_GL_FLOAT_MAT2x4:
948 return 2;
950 case LOCAL_GL_FLOAT_MAT3x2:
951 case LOCAL_GL_FLOAT_MAT3:
952 case LOCAL_GL_FLOAT_MAT3x4:
953 return 3;
955 case LOCAL_GL_FLOAT_MAT4x2:
956 case LOCAL_GL_FLOAT_MAT4x3:
957 case LOCAL_GL_FLOAT_MAT4:
958 return 4;
960 default:
961 return 1;
965 uint8_t ElemTypeComponents(const GLenum elemType) {
966 switch (elemType) {
967 case LOCAL_GL_BOOL:
968 case LOCAL_GL_FLOAT:
969 case LOCAL_GL_INT:
970 case LOCAL_GL_UNSIGNED_INT:
971 case LOCAL_GL_SAMPLER_2D:
972 case LOCAL_GL_SAMPLER_3D:
973 case LOCAL_GL_SAMPLER_CUBE:
974 case LOCAL_GL_SAMPLER_2D_SHADOW:
975 case LOCAL_GL_SAMPLER_2D_ARRAY:
976 case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
977 case LOCAL_GL_SAMPLER_CUBE_SHADOW:
978 case LOCAL_GL_INT_SAMPLER_2D:
979 case LOCAL_GL_INT_SAMPLER_3D:
980 case LOCAL_GL_INT_SAMPLER_CUBE:
981 case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
982 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
983 case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
984 case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
985 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
986 return 1;
988 case LOCAL_GL_BOOL_VEC2:
989 case LOCAL_GL_FLOAT_VEC2:
990 case LOCAL_GL_INT_VEC2:
991 case LOCAL_GL_UNSIGNED_INT_VEC2:
992 return 2;
994 case LOCAL_GL_BOOL_VEC3:
995 case LOCAL_GL_FLOAT_VEC3:
996 case LOCAL_GL_INT_VEC3:
997 case LOCAL_GL_UNSIGNED_INT_VEC3:
998 return 3;
1000 case LOCAL_GL_BOOL_VEC4:
1001 case LOCAL_GL_FLOAT_VEC4:
1002 case LOCAL_GL_INT_VEC4:
1003 case LOCAL_GL_UNSIGNED_INT_VEC4:
1004 case LOCAL_GL_FLOAT_MAT2:
1005 return 4;
1007 case LOCAL_GL_FLOAT_MAT2x3:
1008 case LOCAL_GL_FLOAT_MAT3x2:
1009 return 2 * 3;
1011 case LOCAL_GL_FLOAT_MAT2x4:
1012 case LOCAL_GL_FLOAT_MAT4x2:
1013 return 2 * 4;
1015 case LOCAL_GL_FLOAT_MAT3:
1016 return 3 * 3;
1018 case LOCAL_GL_FLOAT_MAT3x4:
1019 case LOCAL_GL_FLOAT_MAT4x3:
1020 return 3 * 4;
1022 case LOCAL_GL_FLOAT_MAT4:
1023 return 4 * 4;
1025 default:
1026 return 0;
1030 bool WebGLProgram::ValidateAfterTentativeLink(
1031 std::string* const out_linkLog) const {
1032 const auto& linkInfo = mMostRecentLinkInfo;
1033 const auto& gl = mContext->gl;
1035 // Check for overlapping attrib locations.
1037 std::unordered_map<uint32_t, const std::string&> nameByLoc;
1038 for (const auto& attrib : linkInfo->active.activeAttribs) {
1039 if (attrib.location == -1) continue;
1041 const auto& elemType = attrib.elemType;
1042 const auto numUsedLocs = NumUsedLocationsByElemType(elemType);
1043 for (uint32_t i = 0; i < numUsedLocs; i++) {
1044 const uint32_t usedLoc = attrib.location + i;
1046 const auto res = nameByLoc.insert({usedLoc, attrib.name});
1047 const bool& didInsert = res.second;
1048 if (!didInsert) {
1049 const auto& aliasingName = attrib.name;
1050 const auto& itrExisting = res.first;
1051 const auto& existingName = itrExisting->second;
1052 *out_linkLog = nsPrintfCString(
1053 "Attrib \"%s\" aliases locations used by"
1054 " attrib \"%s\".",
1055 aliasingName.c_str(), existingName.c_str())
1056 .BeginReading();
1057 return false;
1063 // Forbid too many components for specified buffer mode
1064 const auto& activeTfVaryings = linkInfo->active.activeTfVaryings;
1065 MOZ_ASSERT(mNextLink_TransformFeedbackVaryings.size() ==
1066 activeTfVaryings.size());
1067 if (!activeTfVaryings.empty()) {
1068 GLuint maxComponentsPerIndex = 0;
1069 switch (linkInfo->transformFeedbackBufferMode) {
1070 case LOCAL_GL_INTERLEAVED_ATTRIBS:
1071 gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS,
1072 &maxComponentsPerIndex);
1073 break;
1075 case LOCAL_GL_SEPARATE_ATTRIBS:
1076 gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS,
1077 &maxComponentsPerIndex);
1078 break;
1080 default:
1081 MOZ_CRASH("`bufferMode`");
1084 std::vector<size_t> componentsPerVert;
1085 for (const auto& cur : activeTfVaryings) {
1086 if (componentsPerVert.empty() ||
1087 linkInfo->transformFeedbackBufferMode == LOCAL_GL_SEPARATE_ATTRIBS) {
1088 componentsPerVert.push_back(0);
1091 size_t varyingComponents = ElemTypeComponents(cur.elemType);
1092 MOZ_ASSERT(varyingComponents);
1093 varyingComponents *= cur.elemCount;
1095 auto& totalComponentsForIndex = *(componentsPerVert.rbegin());
1096 totalComponentsForIndex += varyingComponents;
1098 if (totalComponentsForIndex > maxComponentsPerIndex) {
1099 *out_linkLog = nsPrintfCString(
1100 "Transform feedback varying \"%s\""
1101 " pushed `componentsForIndex` over the"
1102 " limit of %u.",
1103 cur.name.c_str(), maxComponentsPerIndex)
1104 .BeginReading();
1105 return false;
1109 linkInfo->componentsPerTFVert = std::move(componentsPerVert);
1112 return true;
1115 bool WebGLProgram::UseProgram() const {
1116 if (!mMostRecentLinkInfo) {
1117 mContext->ErrorInvalidOperation(
1118 "Program has not been successfully linked.");
1119 return false;
1122 if (mContext->mBoundTransformFeedback &&
1123 mContext->mBoundTransformFeedback->mIsActive &&
1124 !mContext->mBoundTransformFeedback->mIsPaused) {
1125 mContext->ErrorInvalidOperation(
1126 "Transform feedback active and not paused.");
1127 return false;
1130 mContext->gl->fUseProgram(mGLName);
1131 return true;
1134 bool WebGLProgram::ValidateProgram() const {
1135 gl::GLContext* gl = mContext->gl;
1137 #ifdef XP_MACOSX
1138 // See bug 593867 for NVIDIA and bug 657201 for ATI. The latter is confirmed
1139 // with Mac OS 10.6.7.
1140 if (gl->WorkAroundDriverBugs()) {
1141 mContext->GenerateWarning(
1142 "Implemented as a no-op on"
1143 " Mac to work around crashes.");
1144 return true;
1146 #endif
1148 gl->fValidateProgram(mGLName);
1149 GLint ok = 0;
1150 gl->fGetProgramiv(mGLName, LOCAL_GL_VALIDATE_STATUS, &ok);
1151 return bool(ok);
1154 ////////////////////////////////////////////////////////////////////////////////
1156 void WebGLProgram::LinkAndUpdate() {
1157 mMostRecentLinkInfo = nullptr;
1159 gl::GLContext* gl = mContext->gl;
1160 gl->fLinkProgram(mGLName);
1162 // Grab the program log.
1164 GLuint logLenWithNull = 0;
1165 gl->fGetProgramiv(mGLName, LOCAL_GL_INFO_LOG_LENGTH,
1166 (GLint*)&logLenWithNull);
1167 if (logLenWithNull > 1) {
1168 std::vector<char> buffer(logLenWithNull);
1169 gl->fGetProgramInfoLog(mGLName, buffer.size(), nullptr, buffer.data());
1170 mLinkLog = buffer.data();
1171 } else {
1172 mLinkLog.clear();
1176 GLint ok = 0;
1177 gl->fGetProgramiv(mGLName, LOCAL_GL_LINK_STATUS, &ok);
1178 if (!ok) return;
1180 mMostRecentLinkInfo =
1181 QueryProgramInfo(this, gl); // Fallible after context loss.
1184 void WebGLProgram::TransformFeedbackVaryings(
1185 const std::vector<std::string>& varyings, GLenum bufferMode) {
1186 const auto& gl = mContext->gl;
1188 switch (bufferMode) {
1189 case LOCAL_GL_INTERLEAVED_ATTRIBS:
1190 break;
1192 case LOCAL_GL_SEPARATE_ATTRIBS: {
1193 GLuint maxAttribs = 0;
1194 gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS,
1195 &maxAttribs);
1196 if (varyings.size() > maxAttribs) {
1197 mContext->ErrorInvalidValue("Length of `varyings` exceeds %s.",
1198 "TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS");
1199 return;
1201 } break;
1203 default:
1204 mContext->ErrorInvalidEnumInfo("bufferMode", bufferMode);
1205 return;
1208 ////
1210 mNextLink_TransformFeedbackVaryings = varyings;
1211 mNextLink_TransformFeedbackBufferMode = bufferMode;
1214 } // namespace mozilla