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 "WebGLShader.h"
18 #include "WebGLShaderValidator.h"
19 #include "WebGLTransformFeedback.h"
20 #include "WebGLValidateStrings.h"
21 #include "WebGLVertexArray.h"
25 static bool IsShadowSampler(const GLenum elemType
) {
27 case LOCAL_GL_SAMPLER_2D_SHADOW
:
28 case LOCAL_GL_SAMPLER_CUBE_SHADOW
:
29 case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW
:
36 static Maybe
<webgl::TextureBaseType
> SamplerBaseType(const GLenum 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
);
66 static webgl::TextureBaseType
FragOutputBaseType(const GLenum type
) {
69 case LOCAL_GL_FLOAT_VEC2
:
70 case LOCAL_GL_FLOAT_VEC3
:
71 case LOCAL_GL_FLOAT_VEC4
:
72 return webgl::TextureBaseType::Float
;
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
;
90 const auto& str
= EnumString(type
);
91 gfxCriticalError() << "Unhandled enum for FragOutputBaseType: "
93 return webgl::TextureBaseType::Float
;
96 // -----------------------------------------
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
));
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
) {
214 ret
.channelsPerElem
= 1;
215 ret
.pfn
= &UniformAs1fv
;
217 case LOCAL_GL_FLOAT_VEC2
:
218 ret
.channelsPerElem
= 2;
219 ret
.pfn
= &UniformAs2fv
;
221 case LOCAL_GL_FLOAT_VEC3
:
222 ret
.channelsPerElem
= 3;
223 ret
.pfn
= &UniformAs3fv
;
225 case LOCAL_GL_FLOAT_VEC4
:
226 ret
.channelsPerElem
= 4;
227 ret
.pfn
= &UniformAs4fv
;
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
:
247 ret
.channelsPerElem
= 1;
248 ret
.pfn
= &UniformAs1iv
;
250 case LOCAL_GL_BOOL_VEC2
:
251 case LOCAL_GL_INT_VEC2
:
252 ret
.channelsPerElem
= 2;
253 ret
.pfn
= &UniformAs2iv
;
255 case LOCAL_GL_BOOL_VEC3
:
256 case LOCAL_GL_INT_VEC3
:
257 ret
.channelsPerElem
= 3;
258 ret
.pfn
= &UniformAs3iv
;
260 case LOCAL_GL_BOOL_VEC4
:
261 case LOCAL_GL_INT_VEC4
:
262 ret
.channelsPerElem
= 4;
263 ret
.pfn
= &UniformAs4iv
;
266 case LOCAL_GL_UNSIGNED_INT
:
267 ret
.channelsPerElem
= 1;
268 ret
.pfn
= &UniformAs1uiv
;
270 case LOCAL_GL_UNSIGNED_INT_VEC2
:
271 ret
.channelsPerElem
= 2;
272 ret
.pfn
= &UniformAs2uiv
;
274 case LOCAL_GL_UNSIGNED_INT_VEC3
:
275 ret
.channelsPerElem
= 3;
276 ret
.pfn
= &UniformAs3uiv
;
278 case LOCAL_GL_UNSIGNED_INT_VEC4
:
279 ret
.channelsPerElem
= 4;
280 ret
.pfn
= &UniformAs4uiv
;
285 case LOCAL_GL_FLOAT_MAT2
:
286 ret
.channelsPerElem
= 2 * 2;
287 ret
.pfn
= &UniformAsMatrix2x2fv
;
289 case LOCAL_GL_FLOAT_MAT2x3
:
290 ret
.channelsPerElem
= 2 * 3;
291 ret
.pfn
= &UniformAsMatrix2x3fv
;
293 case LOCAL_GL_FLOAT_MAT2x4
:
294 ret
.channelsPerElem
= 2 * 4;
295 ret
.pfn
= &UniformAsMatrix2x4fv
;
298 case LOCAL_GL_FLOAT_MAT3x2
:
299 ret
.channelsPerElem
= 3 * 2;
300 ret
.pfn
= &UniformAsMatrix3x2fv
;
302 case LOCAL_GL_FLOAT_MAT3
:
303 ret
.channelsPerElem
= 3 * 3;
304 ret
.pfn
= &UniformAsMatrix3x3fv
;
306 case LOCAL_GL_FLOAT_MAT3x4
:
307 ret
.channelsPerElem
= 3 * 4;
308 ret
.pfn
= &UniformAsMatrix3x4fv
;
311 case LOCAL_GL_FLOAT_MAT4x2
:
312 ret
.channelsPerElem
= 4 * 2;
313 ret
.pfn
= &UniformAsMatrix4x2fv
;
315 case LOCAL_GL_FLOAT_MAT4x3
:
316 ret
.channelsPerElem
= 4 * 3;
317 ret
.pfn
= &UniformAsMatrix4x3fv
;
319 case LOCAL_GL_FLOAT_MAT4
:
320 ret
.channelsPerElem
= 4 * 4;
321 ret
.pfn
= &UniformAsMatrix4x4fv
;
325 gfxCriticalError() << "Bad `elemType`: " << EnumString(info
.elemType
);
326 MOZ_CRASH("`elemType`");
333 // -------------------------
335 // #define DUMP_SHADERVAR_MAPPINGS
337 RefPtr
<const webgl::LinkedProgramInfo
> QueryProgramInfo(WebGLProgram
* prog
,
339 WebGLContext
* const webgl
= prog
->mContext
;
341 RefPtr
<webgl::LinkedProgramInfo
> info(new webgl::LinkedProgramInfo(prog
));
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;
361 webgl::FragOutputInfo
{uint8_t(loc
), cur
.name
, cur
.mappedName
,
362 FragOutputBaseType(cur
.type
)};
363 if (!cur
.isArray()) {
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
);
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
390 drawBuffers
= webgl
->GLMaxDrawBuffers();
391 } else if (translatedSource
.find("(gl_FragColor") == std::string::npos
&&
392 translatedSource
.find("(webgl_FragColor") ==
394 translatedSource
.find("(gl_FragData") == std::string::npos
&&
395 translatedSource
.find("(webgl_FragData") ==
397 // We have to support no-color-output shaders?
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
};
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
);
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());
432 std::unordered_map
<std::string
, std::string
> nameUnmap
;
433 for (const auto& pair
: nameMap
) {
434 nameUnmap
.insert({pair
.second
, pair
.first
});
438 GetLinkActiveInfo(*gl
, prog
->mGLName
, webgl
->IsWebGL2(), nameUnmap
);
442 for (const auto& attrib
: info
->active
.activeAttribs
) {
443 if (attrib
.location
== 0) {
444 info
->attrib0Active
= true;
449 info
->webgl_gl_VertexID_Offset
=
450 gl
->fGetUniformLocation(prog
->mGLName
, "webgl_gl_VertexID_Offset");
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
);
459 const bool isShadowSampler
= IsShadowSampler(elemType
);
461 auto* texList
= &webgl
->mBound2DTextures
;
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
:
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
;
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
;
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
;
491 auto curInfo
= std::unique_ptr
<webgl::SamplerUniformInfo
>(
492 new webgl::SamplerUniformInfo
{*texList
, *baseType
, isShadowSampler
});
493 MOZ_RELEASE_ASSERT(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
}});
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
);
521 ////////////////////////////////////////////////////////////////////////////////
523 webgl::LinkedProgramInfo::LinkedProgramInfo(WebGLProgram
* prog
)
525 transformFeedbackBufferMode(prog
->mNextLink_TransformFeedbackBufferMode
) {
528 webgl::LinkedProgramInfo::~LinkedProgramInfo() = default;
530 webgl::AttribBaseType
webgl::ToAttribBaseType(const GLenum elemType
) {
533 case LOCAL_GL_BOOL_VEC2
:
534 case LOCAL_GL_BOOL_VEC3
:
535 case LOCAL_GL_BOOL_VEC4
:
536 return webgl::AttribBaseType::Boolean
;
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
;
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
;
581 gfxCriticalError() << "Bad `elemType`: " << EnumString(elemType
);
582 MOZ_CRASH("`elemType`");
586 const char* webgl::ToString(const webgl::AttribBaseType x
) {
588 case webgl::AttribBaseType::Float
:
590 case webgl::AttribBaseType::Int
:
592 case webgl::AttribBaseType::Uint
:
594 case webgl::AttribBaseType::Boolean
:
597 MOZ_CRASH("pacify gcc6 warning");
600 const char* webgl::ToString(const webgl::UniformBaseType x
) {
602 case webgl::UniformBaseType::Float
:
604 case webgl::UniformBaseType::Int
:
606 case webgl::UniformBaseType::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();
622 webgl
->ErrorInvalidOperation(
623 "Vertex attrib array %u is enabled but"
624 " has no buffer bound.",
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
) {
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.
670 fetchLimits
.maxVerts
= std::min(fetchLimits
.maxVerts
, availElems
);
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
);
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.");
698 mScratchFetchLimits
= std::move(fetchLimits
);
699 return &mScratchFetchLimits
;
702 ////////////////////////////////////////////////////////////////////////////////
705 WebGLProgram::WebGLProgram(WebGLContext
* webgl
)
706 : WebGLContextBoundObject(webgl
),
707 mGLName(webgl
->gl
->fCreateProgram()),
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 ////////////////////////////////////////////////////////////////////////////////
724 void WebGLProgram::AttachShader(WebGLShader
& shader
) {
725 RefPtr
<WebGLShader
>* shaderSlot
= nullptr;
726 switch (shader
.mType
) {
727 case LOCAL_GL_VERTEX_SHADER
:
728 shaderSlot
= &mVertShader
;
730 case LOCAL_GL_FRAGMENT_SHADER
:
731 shaderSlot
= &mFragShader
;
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
);
744 mContext
->GenerateError(err
->type
, "%s", err
->info
.c_str());
748 if (loc
>= mContext
->MaxVertexAttribs()) {
749 mContext
->ErrorInvalidValue(
750 "`location` must be less than"
751 " MAX_VERTEX_ATTRIBS.");
755 if (name
.find("gl_") == 0) {
756 mContext
->ErrorInvalidOperation(
757 "Can't set the location of a"
758 " name that starts with 'gl_'.");
762 auto res
= mNextLink_BoundAttribLocs
.insert({name
, loc
});
764 const auto& wasInserted
= res
.second
;
766 const auto& itr
= res
.first
;
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
;
777 case LOCAL_GL_FRAGMENT_SHADER
:
778 shaderSlot
= &mFragShader
;
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 {
793 mContext
->ErrorInvalidOperation("`program` must be linked.");
797 auto& uniformBlocks
= LinkInfo()->uniformBlocks
;
798 if (uniformBlockIndex
>= uniformBlocks
.size()) {
799 mContext
->ErrorInvalidValue("Index %u invalid.", uniformBlockIndex
);
802 auto& uniformBlock
= uniformBlocks
[uniformBlockIndex
];
804 const auto& indexedBindings
= mContext
->mIndexedUniformBufferBindings
;
805 if (uniformBlockBinding
>= indexedBindings
.size()) {
806 mContext
->ErrorInvalidValue("Binding %u invalid.", uniformBlockBinding
);
809 const auto& indexedBinding
= indexedBindings
[uniformBlockBinding
];
813 gl::GLContext
* gl
= mContext
->GL();
814 gl
->fUniformBlockBinding(mGLName
, uniformBlockIndex
, uniformBlockBinding
);
818 uniformBlock
.binding
= &indexedBinding
;
821 bool WebGLProgram::ValidateForLink() {
822 const auto AppendCompileLog
= [&](const WebGLShader
* const shader
) {
824 mLinkLog
+= " Missing shader.";
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
);
836 const auto& vertInfo
= *mVertShader
->CompileResults();
838 if (!mFragShader
|| !mFragShader
->IsCompiled()) {
839 mLinkLog
= "Must have a compiled fragment shader attached:";
840 AppendCompileLog(mFragShader
);
843 const auto& fragInfo
= *mFragShader
->CompileResults();
846 if (!fragInfo
.CanLinkTo(vertInfo
, &errInfo
)) {
847 mLinkLog
= errInfo
.BeginReading();
851 const auto& gl
= mContext
->gl
;
853 if (gl
->WorkAroundDriverBugs() && mContext
->mIsMesa
) {
854 // Bug 1203135: Mesa crashes internally if we exceed the reported maximum
856 if (mVertShader
->NumAttributes() > mContext
->MaxVertexAttribs()) {
858 "Number of attributes exceeds Mesa's reported max"
867 void WebGLProgram::LinkProgram() {
868 if (mNumActiveTFOs
) {
869 mContext
->ErrorInvalidOperation(
870 "Program is in-use by one or more active"
871 " transform feedback objects.");
875 // as some of the validation changes program state
878 mMostRecentLinkInfo
= nullptr;
880 if (!ValidateForLink()) {
881 mContext
->GenerateWarning("%s", mLinkLog
.c_str());
885 // Bind the attrib locations.
886 // This can't be done trivially, because we have to deal with mapped attrib
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
);
916 if (mMostRecentLinkInfo
) {
917 std::string postLinkLog
;
918 if (ValidateAfterTentativeLink(&postLinkLog
)) return;
920 mMostRecentLinkInfo
= nullptr;
921 mLinkLog
= std::move(postLinkLog
);
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"
940 static uint8_t NumUsedLocationsByElemType(GLenum elemType
) {
944 case LOCAL_GL_FLOAT_MAT2
:
945 case LOCAL_GL_FLOAT_MAT2x3
:
946 case LOCAL_GL_FLOAT_MAT2x4
:
949 case LOCAL_GL_FLOAT_MAT3x2
:
950 case LOCAL_GL_FLOAT_MAT3
:
951 case LOCAL_GL_FLOAT_MAT3x4
:
954 case LOCAL_GL_FLOAT_MAT4x2
:
955 case LOCAL_GL_FLOAT_MAT4x3
:
956 case LOCAL_GL_FLOAT_MAT4
:
964 uint8_t ElemTypeComponents(const GLenum elemType
) {
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
:
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
:
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
:
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
:
1006 case LOCAL_GL_FLOAT_MAT2x3
:
1007 case LOCAL_GL_FLOAT_MAT3x2
:
1010 case LOCAL_GL_FLOAT_MAT2x4
:
1011 case LOCAL_GL_FLOAT_MAT4x2
:
1014 case LOCAL_GL_FLOAT_MAT3
:
1017 case LOCAL_GL_FLOAT_MAT3x4
:
1018 case LOCAL_GL_FLOAT_MAT4x3
:
1021 case LOCAL_GL_FLOAT_MAT4
:
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
;
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"
1054 aliasingName
.c_str(), existingName
.c_str())
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
);
1074 case LOCAL_GL_SEPARATE_ATTRIBS
:
1075 gl
->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS
,
1076 &maxComponentsPerIndex
);
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"
1102 cur
.name
.c_str(), maxComponentsPerIndex
)
1108 linkInfo
->componentsPerTFVert
= std::move(componentsPerVert
);
1114 bool WebGLProgram::UseProgram() const {
1115 if (!mMostRecentLinkInfo
) {
1116 mContext
->ErrorInvalidOperation(
1117 "Program has not been successfully linked.");
1121 if (mContext
->mBoundTransformFeedback
&&
1122 mContext
->mBoundTransformFeedback
->mIsActive
&&
1123 !mContext
->mBoundTransformFeedback
->mIsPaused
) {
1124 mContext
->ErrorInvalidOperation(
1125 "Transform feedback active and not paused.");
1129 mContext
->gl
->fUseProgram(mGLName
);
1133 bool WebGLProgram::ValidateProgram() const {
1134 gl::GLContext
* gl
= mContext
->gl
;
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.");
1147 gl
->fValidateProgram(mGLName
);
1149 gl
->fGetProgramiv(mGLName
, LOCAL_GL_VALIDATE_STATUS
, &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();
1176 gl
->fGetProgramiv(mGLName
, LOCAL_GL_LINK_STATUS
, &ok
);
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
:
1191 case LOCAL_GL_SEPARATE_ATTRIBS
: {
1192 GLuint maxAttribs
= 0;
1193 gl
->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS
,
1195 if (varyings
.size() > maxAttribs
) {
1196 mContext
->ErrorInvalidValue("Length of `varyings` exceeds %s.",
1197 "TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS");
1203 mContext
->ErrorInvalidEnumInfo("bufferMode", bufferMode
);
1209 mNextLink_TransformFeedbackVaryings
= varyings
;
1210 mNextLink_TransformFeedbackBufferMode
= bufferMode
;
1213 } // namespace mozilla