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"
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"
26 static bool IsShadowSampler(const GLenum elemType
) {
28 case LOCAL_GL_SAMPLER_2D_SHADOW
:
29 case LOCAL_GL_SAMPLER_CUBE_SHADOW
:
30 case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW
:
37 static Maybe
<webgl::TextureBaseType
> SamplerBaseType(const GLenum 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
);
67 static webgl::TextureBaseType
FragOutputBaseType(const GLenum type
) {
70 case LOCAL_GL_FLOAT_VEC2
:
71 case LOCAL_GL_FLOAT_VEC3
:
72 case LOCAL_GL_FLOAT_VEC4
:
73 return webgl::TextureBaseType::Float
;
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
;
91 const auto& str
= EnumString(type
);
92 gfxCriticalError() << "Unhandled enum for FragOutputBaseType: "
94 return webgl::TextureBaseType::Float
;
97 // -----------------------------------------
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
));
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
) {
215 ret
.channelsPerElem
= 1;
216 ret
.pfn
= &UniformAs1fv
;
218 case LOCAL_GL_FLOAT_VEC2
:
219 ret
.channelsPerElem
= 2;
220 ret
.pfn
= &UniformAs2fv
;
222 case LOCAL_GL_FLOAT_VEC3
:
223 ret
.channelsPerElem
= 3;
224 ret
.pfn
= &UniformAs3fv
;
226 case LOCAL_GL_FLOAT_VEC4
:
227 ret
.channelsPerElem
= 4;
228 ret
.pfn
= &UniformAs4fv
;
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
:
248 ret
.channelsPerElem
= 1;
249 ret
.pfn
= &UniformAs1iv
;
251 case LOCAL_GL_BOOL_VEC2
:
252 case LOCAL_GL_INT_VEC2
:
253 ret
.channelsPerElem
= 2;
254 ret
.pfn
= &UniformAs2iv
;
256 case LOCAL_GL_BOOL_VEC3
:
257 case LOCAL_GL_INT_VEC3
:
258 ret
.channelsPerElem
= 3;
259 ret
.pfn
= &UniformAs3iv
;
261 case LOCAL_GL_BOOL_VEC4
:
262 case LOCAL_GL_INT_VEC4
:
263 ret
.channelsPerElem
= 4;
264 ret
.pfn
= &UniformAs4iv
;
267 case LOCAL_GL_UNSIGNED_INT
:
268 ret
.channelsPerElem
= 1;
269 ret
.pfn
= &UniformAs1uiv
;
271 case LOCAL_GL_UNSIGNED_INT_VEC2
:
272 ret
.channelsPerElem
= 2;
273 ret
.pfn
= &UniformAs2uiv
;
275 case LOCAL_GL_UNSIGNED_INT_VEC3
:
276 ret
.channelsPerElem
= 3;
277 ret
.pfn
= &UniformAs3uiv
;
279 case LOCAL_GL_UNSIGNED_INT_VEC4
:
280 ret
.channelsPerElem
= 4;
281 ret
.pfn
= &UniformAs4uiv
;
286 case LOCAL_GL_FLOAT_MAT2
:
287 ret
.channelsPerElem
= 2 * 2;
288 ret
.pfn
= &UniformAsMatrix2x2fv
;
290 case LOCAL_GL_FLOAT_MAT2x3
:
291 ret
.channelsPerElem
= 2 * 3;
292 ret
.pfn
= &UniformAsMatrix2x3fv
;
294 case LOCAL_GL_FLOAT_MAT2x4
:
295 ret
.channelsPerElem
= 2 * 4;
296 ret
.pfn
= &UniformAsMatrix2x4fv
;
299 case LOCAL_GL_FLOAT_MAT3x2
:
300 ret
.channelsPerElem
= 3 * 2;
301 ret
.pfn
= &UniformAsMatrix3x2fv
;
303 case LOCAL_GL_FLOAT_MAT3
:
304 ret
.channelsPerElem
= 3 * 3;
305 ret
.pfn
= &UniformAsMatrix3x3fv
;
307 case LOCAL_GL_FLOAT_MAT3x4
:
308 ret
.channelsPerElem
= 3 * 4;
309 ret
.pfn
= &UniformAsMatrix3x4fv
;
312 case LOCAL_GL_FLOAT_MAT4x2
:
313 ret
.channelsPerElem
= 4 * 2;
314 ret
.pfn
= &UniformAsMatrix4x2fv
;
316 case LOCAL_GL_FLOAT_MAT4x3
:
317 ret
.channelsPerElem
= 4 * 3;
318 ret
.pfn
= &UniformAsMatrix4x3fv
;
320 case LOCAL_GL_FLOAT_MAT4
:
321 ret
.channelsPerElem
= 4 * 4;
322 ret
.pfn
= &UniformAsMatrix4x4fv
;
326 gfxCriticalError() << "Bad `elemType`: " << EnumString(info
.elemType
);
327 MOZ_CRASH("`elemType`");
334 // -------------------------
336 // #define DUMP_SHADERVAR_MAPPINGS
338 RefPtr
<const webgl::LinkedProgramInfo
> QueryProgramInfo(WebGLProgram
* prog
,
340 WebGLContext
* const webgl
= prog
->mContext
;
342 RefPtr
<webgl::LinkedProgramInfo
> info(new webgl::LinkedProgramInfo(prog
));
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;
362 webgl::FragOutputInfo
{uint8_t(loc
), cur
.name
, cur
.mappedName
,
363 FragOutputBaseType(cur
.type
)};
364 if (!cur
.isArray()) {
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
);
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
391 drawBuffers
= webgl
->GLMaxDrawBuffers();
392 } else if (translatedSource
.find("(gl_FragColor") == std::string::npos
&&
393 translatedSource
.find("(webgl_FragColor") ==
395 translatedSource
.find("(gl_FragData") == std::string::npos
&&
396 translatedSource
.find("(webgl_FragData") ==
398 // We have to support no-color-output shaders?
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
};
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
);
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());
433 std::unordered_map
<std::string
, std::string
> nameUnmap
;
434 for (const auto& pair
: nameMap
) {
435 nameUnmap
.insert({pair
.second
, pair
.first
});
439 GetLinkActiveInfo(*gl
, prog
->mGLName
, webgl
->IsWebGL2(), nameUnmap
);
443 for (const auto& attrib
: info
->active
.activeAttribs
) {
444 if (attrib
.location
== 0) {
445 info
->attrib0Active
= true;
450 info
->webgl_gl_VertexID_Offset
=
451 gl
->fGetUniformLocation(prog
->mGLName
, "webgl_gl_VertexID_Offset");
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
);
460 const bool isShadowSampler
= IsShadowSampler(elemType
);
462 auto* texList
= &webgl
->mBound2DTextures
;
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
:
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
;
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
;
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
;
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
}});
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
);
522 ////////////////////////////////////////////////////////////////////////////////
524 webgl::LinkedProgramInfo::LinkedProgramInfo(WebGLProgram
* prog
)
526 transformFeedbackBufferMode(prog
->mNextLink_TransformFeedbackBufferMode
) {
529 webgl::LinkedProgramInfo::~LinkedProgramInfo() = default;
531 webgl::AttribBaseType
webgl::ToAttribBaseType(const GLenum elemType
) {
534 case LOCAL_GL_BOOL_VEC2
:
535 case LOCAL_GL_BOOL_VEC3
:
536 case LOCAL_GL_BOOL_VEC4
:
537 return webgl::AttribBaseType::Boolean
;
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
;
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
;
582 gfxCriticalError() << "Bad `elemType`: " << EnumString(elemType
);
583 MOZ_CRASH("`elemType`");
587 const char* webgl::ToString(const webgl::AttribBaseType x
) {
589 case webgl::AttribBaseType::Float
:
591 case webgl::AttribBaseType::Int
:
593 case webgl::AttribBaseType::Uint
:
595 case webgl::AttribBaseType::Boolean
:
598 MOZ_CRASH("pacify gcc6 warning");
601 const char* webgl::ToString(const webgl::UniformBaseType x
) {
603 case webgl::UniformBaseType::Float
:
605 case webgl::UniformBaseType::Int
:
607 case webgl::UniformBaseType::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();
623 webgl
->ErrorInvalidOperation(
624 "Vertex attrib array %u is enabled but"
625 " has no buffer bound.",
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
) {
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.
671 fetchLimits
.maxVerts
= std::min(fetchLimits
.maxVerts
, availElems
);
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
);
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.");
699 mScratchFetchLimits
= std::move(fetchLimits
);
700 return &mScratchFetchLimits
;
703 ////////////////////////////////////////////////////////////////////////////////
706 WebGLProgram::WebGLProgram(WebGLContext
* webgl
)
707 : WebGLContextBoundObject(webgl
),
708 mGLName(webgl
->gl
->fCreateProgram()),
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 ////////////////////////////////////////////////////////////////////////////////
725 void WebGLProgram::AttachShader(WebGLShader
& shader
) {
726 RefPtr
<WebGLShader
>* shaderSlot
= nullptr;
727 switch (shader
.mType
) {
728 case LOCAL_GL_VERTEX_SHADER
:
729 shaderSlot
= &mVertShader
;
731 case LOCAL_GL_FRAGMENT_SHADER
:
732 shaderSlot
= &mFragShader
;
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
);
745 mContext
->GenerateError(err
->type
, "%s", err
->info
.c_str());
749 if (loc
>= mContext
->MaxVertexAttribs()) {
750 mContext
->ErrorInvalidValue(
751 "`location` must be less than"
752 " MAX_VERTEX_ATTRIBS.");
756 if (name
.find("gl_") == 0) {
757 mContext
->ErrorInvalidOperation(
758 "Can't set the location of a"
759 " name that starts with 'gl_'.");
763 auto res
= mNextLink_BoundAttribLocs
.insert({name
, loc
});
765 const auto& wasInserted
= res
.second
;
767 const auto& itr
= res
.first
;
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
;
778 case LOCAL_GL_FRAGMENT_SHADER
:
779 shaderSlot
= &mFragShader
;
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 {
794 mContext
->ErrorInvalidOperation("`program` must be linked.");
798 auto& uniformBlocks
= LinkInfo()->uniformBlocks
;
799 if (uniformBlockIndex
>= uniformBlocks
.size()) {
800 mContext
->ErrorInvalidValue("Index %u invalid.", uniformBlockIndex
);
803 auto& uniformBlock
= uniformBlocks
[uniformBlockIndex
];
805 const auto& indexedBindings
= mContext
->mIndexedUniformBufferBindings
;
806 if (uniformBlockBinding
>= indexedBindings
.size()) {
807 mContext
->ErrorInvalidValue("Binding %u invalid.", uniformBlockBinding
);
810 const auto& indexedBinding
= indexedBindings
[uniformBlockBinding
];
814 gl::GLContext
* gl
= mContext
->GL();
815 gl
->fUniformBlockBinding(mGLName
, uniformBlockIndex
, uniformBlockBinding
);
819 uniformBlock
.binding
= &indexedBinding
;
822 bool WebGLProgram::ValidateForLink() {
823 const auto AppendCompileLog
= [&](const WebGLShader
* const shader
) {
825 mLinkLog
+= " Missing shader.";
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
);
837 const auto& vertInfo
= *mVertShader
->CompileResults();
839 if (!mFragShader
|| !mFragShader
->IsCompiled()) {
840 mLinkLog
= "Must have a compiled fragment shader attached:";
841 AppendCompileLog(mFragShader
);
844 const auto& fragInfo
= *mFragShader
->CompileResults();
847 if (!fragInfo
.CanLinkTo(vertInfo
, &errInfo
)) {
848 mLinkLog
= errInfo
.BeginReading();
852 const auto& gl
= mContext
->gl
;
854 if (gl
->WorkAroundDriverBugs() && mContext
->mIsMesa
) {
855 // Bug 1203135: Mesa crashes internally if we exceed the reported maximum
857 if (mVertShader
->NumAttributes() > mContext
->MaxVertexAttribs()) {
859 "Number of attributes exceeds Mesa's reported max"
868 void WebGLProgram::LinkProgram() {
869 if (mNumActiveTFOs
) {
870 mContext
->ErrorInvalidOperation(
871 "Program is in-use by one or more active"
872 " transform feedback objects.");
876 // as some of the validation changes program state
879 mMostRecentLinkInfo
= nullptr;
881 if (!ValidateForLink()) {
882 mContext
->GenerateWarning("%s", mLinkLog
.c_str());
886 // Bind the attrib locations.
887 // This can't be done trivially, because we have to deal with mapped attrib
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
);
917 if (mMostRecentLinkInfo
) {
918 std::string postLinkLog
;
919 if (ValidateAfterTentativeLink(&postLinkLog
)) return;
921 mMostRecentLinkInfo
= nullptr;
922 mLinkLog
= std::move(postLinkLog
);
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"
941 static uint8_t NumUsedLocationsByElemType(GLenum elemType
) {
945 case LOCAL_GL_FLOAT_MAT2
:
946 case LOCAL_GL_FLOAT_MAT2x3
:
947 case LOCAL_GL_FLOAT_MAT2x4
:
950 case LOCAL_GL_FLOAT_MAT3x2
:
951 case LOCAL_GL_FLOAT_MAT3
:
952 case LOCAL_GL_FLOAT_MAT3x4
:
955 case LOCAL_GL_FLOAT_MAT4x2
:
956 case LOCAL_GL_FLOAT_MAT4x3
:
957 case LOCAL_GL_FLOAT_MAT4
:
965 uint8_t ElemTypeComponents(const GLenum elemType
) {
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
:
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
:
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
:
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
:
1007 case LOCAL_GL_FLOAT_MAT2x3
:
1008 case LOCAL_GL_FLOAT_MAT3x2
:
1011 case LOCAL_GL_FLOAT_MAT2x4
:
1012 case LOCAL_GL_FLOAT_MAT4x2
:
1015 case LOCAL_GL_FLOAT_MAT3
:
1018 case LOCAL_GL_FLOAT_MAT3x4
:
1019 case LOCAL_GL_FLOAT_MAT4x3
:
1022 case LOCAL_GL_FLOAT_MAT4
:
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
;
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"
1055 aliasingName
.c_str(), existingName
.c_str())
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
);
1075 case LOCAL_GL_SEPARATE_ATTRIBS
:
1076 gl
->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS
,
1077 &maxComponentsPerIndex
);
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"
1103 cur
.name
.c_str(), maxComponentsPerIndex
)
1109 linkInfo
->componentsPerTFVert
= std::move(componentsPerVert
);
1115 bool WebGLProgram::UseProgram() const {
1116 if (!mMostRecentLinkInfo
) {
1117 mContext
->ErrorInvalidOperation(
1118 "Program has not been successfully linked.");
1122 if (mContext
->mBoundTransformFeedback
&&
1123 mContext
->mBoundTransformFeedback
->mIsActive
&&
1124 !mContext
->mBoundTransformFeedback
->mIsPaused
) {
1125 mContext
->ErrorInvalidOperation(
1126 "Transform feedback active and not paused.");
1130 mContext
->gl
->fUseProgram(mGLName
);
1134 bool WebGLProgram::ValidateProgram() const {
1135 gl::GLContext
* gl
= mContext
->gl
;
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.");
1148 gl
->fValidateProgram(mGLName
);
1150 gl
->fGetProgramiv(mGLName
, LOCAL_GL_VALIDATE_STATUS
, &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();
1177 gl
->fGetProgramiv(mGLName
, LOCAL_GL_LINK_STATUS
, &ok
);
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
:
1192 case LOCAL_GL_SEPARATE_ATTRIBS
: {
1193 GLuint maxAttribs
= 0;
1194 gl
->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS
,
1196 if (varyings
.size() > maxAttribs
) {
1197 mContext
->ErrorInvalidValue("Length of `varyings` exceeds %s.",
1198 "TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS");
1204 mContext
->ErrorInvalidEnumInfo("bufferMode", bufferMode
);
1210 mNextLink_TransformFeedbackVaryings
= varyings
;
1211 mNextLink_TransformFeedbackBufferMode
= bufferMode
;
1214 } // namespace mozilla