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 "WebGLShaderValidator.h"
9 #include "mozilla/gfx/Logging.h"
10 #include "mozilla/Preferences.h"
11 #include "mozilla/StaticPrefs_webgl.h"
12 #include "MurmurHash3.h"
13 #include "nsPrintfCString.h"
16 #include "WebGLContext.h"
21 uint64_t IdentifierHashFunc(const char* name
, size_t len
) {
22 // NB: we use the x86 function everywhere, even though it's suboptimal perf
23 // on x64. They return different results; not sure if that's a requirement.
25 MurmurHash3_x86_128(name
, len
, 0, hash
);
29 static ShCompileOptions
ChooseValidatorCompileOptions(
30 const ShBuiltInResources
& resources
, const mozilla::gl::GLContext
* gl
) {
31 ShCompileOptions options
= {};
32 options
.variables
= true;
33 options
.enforcePackingRestrictions
= true;
34 options
.objectCode
= true;
35 options
.initGLPosition
= true;
36 options
.initializeUninitializedLocals
= true;
37 options
.initOutputVariables
= true;
40 options
.removeInvariantAndCentroidForESSL3
= true;
42 // We want to do this everywhere, but to do this on Mac, we need
43 // to do it only on Mac OSX > 10.6 as this causes the shader
44 // compiler in 10.6 to crash
45 options
.clampIndirectArrayBounds
= true;
48 if (gl
->WorkAroundDriverBugs()) {
50 // Work around https://bugs.webkit.org/show_bug.cgi?id=124684,
51 // https://chromium.googlesource.com/angle/angle/+/5e70cf9d0b1bb
52 options
.unfoldShortCircuit
= true;
54 // Work around that Mac drivers handle struct scopes incorrectly.
55 options
.regenerateStructNames
= true;
56 options
.initOutputVariables
= true;
57 options
.initGLPointSize
= true;
59 if (gl
->Vendor() == gl::GLVendor::Intel
) {
60 // Work around that Intel drivers on Mac OSX handle for-loop
62 options
.addAndTrueToLoopCondition
= true;
64 options
.rewriteTexelFetchOffsetToTexelFetch
= true;
68 if (!gl
->IsANGLE() && gl
->Vendor() == gl::GLVendor::Intel
) {
69 // Failures on at least Windows+Intel+OGL on:
70 // conformance/glsl/constructors/glsl-construct-mat2.html
71 options
.scalarizeVecAndMatConstructorArgs
= true;
77 if (resources
.MaxExpressionComplexity
> 0) {
78 options
.limitExpressionComplexity
= true;
80 if (resources
.MaxCallStackDepth
> 0) {
81 options
.limitCallStackDepth
= true;
89 ////////////////////////////////////////
91 static ShShaderOutput
ShaderOutput(gl::GLContext
* gl
) {
93 return SH_ESSL_OUTPUT
;
95 uint32_t version
= gl
->ShadingLanguageVersion();
98 return SH_GLSL_COMPATIBILITY_OUTPUT
;
100 return SH_GLSL_COMPATIBILITY_OUTPUT
;
102 return SH_GLSL_130_OUTPUT
;
104 return SH_GLSL_140_OUTPUT
;
106 return SH_GLSL_150_CORE_OUTPUT
;
108 return SH_GLSL_330_CORE_OUTPUT
;
110 return SH_GLSL_400_CORE_OUTPUT
;
112 return SH_GLSL_410_CORE_OUTPUT
;
114 return SH_GLSL_420_CORE_OUTPUT
;
116 return SH_GLSL_430_CORE_OUTPUT
;
118 return SH_GLSL_440_CORE_OUTPUT
;
120 if (version
>= 450) {
121 // "OpenGL 4.6 is also guaranteed to support all previous versions of
122 // the OpenGL Shading Language back to version 1.10."
123 return SH_GLSL_450_CORE_OUTPUT
;
125 gfxCriticalNote
<< "Unexpected GLSL version: " << version
;
128 return SH_GLSL_COMPATIBILITY_OUTPUT
;
131 std::unique_ptr
<webgl::ShaderValidator
> WebGLContext::CreateShaderValidator(
132 GLenum shaderType
) const {
133 const auto spec
= (IsWebGL2() ? SH_WEBGL2_SPEC
: SH_WEBGL_SPEC
);
134 const auto outputLanguage
= ShaderOutput(gl
);
136 ShBuiltInResources resources
;
137 memset(&resources
, 0, sizeof(resources
));
138 sh::InitBuiltInResources(&resources
);
140 resources
.HashFunction
= webgl::IdentifierHashFunc
;
142 const auto& limits
= Limits();
144 resources
.MaxVertexAttribs
= limits
.maxVertexAttribs
;
145 resources
.MaxVertexUniformVectors
= mGLMaxVertexUniformVectors
;
146 resources
.MaxVertexTextureImageUnits
= mGLMaxVertexTextureImageUnits
;
147 resources
.MaxCombinedTextureImageUnits
= limits
.maxTexUnits
;
148 resources
.MaxTextureImageUnits
= mGLMaxFragmentTextureImageUnits
;
149 resources
.MaxFragmentUniformVectors
= mGLMaxFragmentUniformVectors
;
151 resources
.MaxVertexOutputVectors
= mGLMaxVertexOutputVectors
;
152 resources
.MaxFragmentInputVectors
= mGLMaxFragmentInputVectors
;
153 resources
.MaxVaryingVectors
= mGLMaxFragmentInputVectors
;
156 resources
.MinProgramTexelOffset
= mGLMinProgramTexelOffset
;
157 resources
.MaxProgramTexelOffset
= mGLMaxProgramTexelOffset
;
160 resources
.MaxDrawBuffers
= MaxValidDrawBuffers();
162 if (IsExtensionEnabled(WebGLExtensionID::EXT_frag_depth
))
163 resources
.EXT_frag_depth
= 1;
165 if (IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives
))
166 resources
.OES_standard_derivatives
= 1;
168 if (IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers
))
169 resources
.EXT_draw_buffers
= 1;
171 if (IsExtensionEnabled(WebGLExtensionID::EXT_shader_texture_lod
))
172 resources
.EXT_shader_texture_lod
= 1;
174 if (IsExtensionEnabled(WebGLExtensionID::OVR_multiview2
)) {
175 resources
.OVR_multiview
= 1;
176 resources
.OVR_multiview2
= 1;
177 resources
.MaxViewsOVR
= limits
.maxMultiviewLayers
;
180 // Tell ANGLE to allow highp in frag shaders. (unless disabled)
181 // If underlying GLES doesn't have highp in frag shaders, it should complain
183 resources
.FragmentPrecisionHigh
= mDisableFragHighP
? 0 : 1;
185 if (gl
->WorkAroundDriverBugs()) {
187 if (gl
->Vendor() == gl::GLVendor::NVIDIA
) {
188 // Work around bug 890432
189 resources
.MaxExpressionComplexity
= 1000;
194 const auto compileOptions
=
195 webgl::ChooseValidatorCompileOptions(resources
, gl
);
196 auto ret
= webgl::ShaderValidator::Create(shaderType
, spec
, outputLanguage
,
197 resources
, compileOptions
);
198 if (!ret
) return ret
;
200 ret
->mIfNeeded_webgl_gl_VertexID_Offset
|=
201 mBug_DrawArraysInstancedUserAttribFetchAffectedByFirst
;
206 ////////////////////////////////////////
211 std::unique_ptr
<ShaderValidator
> ShaderValidator::Create(
212 GLenum shaderType
, ShShaderSpec spec
, ShShaderOutput outputLanguage
,
213 const ShBuiltInResources
& resources
, ShCompileOptions compileOptions
) {
215 sh::ConstructCompiler(shaderType
, spec
, outputLanguage
, &resources
);
216 MOZ_RELEASE_ASSERT(handle
);
217 if (!handle
) return nullptr;
219 return std::unique_ptr
<ShaderValidator
>(
220 new ShaderValidator(handle
, compileOptions
, resources
.MaxVaryingVectors
));
223 ShaderValidator::~ShaderValidator() { sh::Destruct(mHandle
); }
225 inline bool StartsWith(const std::string_view
& str
,
226 const std::string_view
& part
) {
227 return str
.find(part
) == 0;
230 inline std::vector
<std::string_view
> Split(std::string_view src
,
231 const std::string_view
& delim
,
232 const size_t maxSplits
= -1) {
233 std::vector
<std::string_view
> ret
;
234 for (const auto i
: IntegerRange(maxSplits
)) {
236 const auto end
= src
.find(delim
);
237 if (end
== size_t(-1)) {
240 ret
.push_back(src
.substr(0, end
));
241 src
= src
.substr(end
+ delim
.size());
247 std::unique_ptr
<const ShaderValidatorResults
>
248 ShaderValidator::ValidateAndTranslate(const char* const source
) const {
249 auto ret
= std::make_unique
<ShaderValidatorResults
>();
251 const std::array
<const char*, 1> parts
= {source
};
253 sh::Compile(mHandle
, parts
.data(), parts
.size(), mCompileOptions
);
255 ret
->mInfoLog
= sh::GetInfoLog(mHandle
);
258 ret
->mObjectCode
= sh::GetObjectCode(mHandle
);
259 ret
->mShaderVersion
= sh::GetShaderVersion(mHandle
);
260 ret
->mVertexShaderNumViews
= sh::GetVertexShaderNumViews(mHandle
);
262 ret
->mAttributes
= *sh::GetAttributes(mHandle
);
263 ret
->mInterfaceBlocks
= *sh::GetInterfaceBlocks(mHandle
);
264 ret
->mOutputVariables
= *sh::GetOutputVariables(mHandle
);
265 ret
->mUniforms
= *sh::GetUniforms(mHandle
);
266 ret
->mVaryings
= *sh::GetVaryings(mHandle
);
268 ret
->mMaxVaryingVectors
= mMaxVaryingVectors
;
270 const auto& nameMap
= *sh::GetNameHashingMap(mHandle
);
271 for (const auto& pair
: nameMap
) {
272 ret
->mNameMap
.insert(pair
);
276 // Custom translation steps
277 auto* const translatedSource
= &ret
->mObjectCode
;
279 // gl_VertexID -> webgl_gl_VertexID
280 // gl_InstanceID -> webgl_gl_InstanceID
283 std::string_view body
= *translatedSource
;
284 if (StartsWith(body
, "#version")) {
285 const auto parts
= Split(body
, "\n", 1);
286 header
= parts
.at(0);
291 for (const auto& attrib
: ret
->mAttributes
) {
292 if (mIfNeeded_webgl_gl_VertexID_Offset
&& attrib
.name
== "gl_VertexID" &&
294 header
+= "uniform int webgl_gl_VertexID_Offset;\n";
296 "#define gl_VertexID (gl_VertexID + webgl_gl_VertexID_Offset)\n";
297 ret
->mNeeds_webgl_gl_VertexID_Offset
= true;
302 auto combined
= header
;
304 *translatedSource
= combined
;
308 sh::ClearResults(mHandle
);
312 bool ShaderValidatorResults::CanLinkTo(const ShaderValidatorResults
& vert
,
313 nsCString
* const out_log
) const {
315 MOZ_ASSERT(vert
.mValid
);
317 if (vert
.mShaderVersion
!= mShaderVersion
) {
318 nsPrintfCString
error(
319 "Vertex shader version %d does not match"
320 " fragment shader version %d.",
321 vert
.mShaderVersion
, mShaderVersion
);
326 for (const auto& itrFrag
: mUniforms
) {
327 for (const auto& itrVert
: vert
.mUniforms
) {
328 if (itrVert
.name
!= itrFrag
.name
) continue;
330 if (!itrVert
.isSameUniformAtLinkTime(itrFrag
)) {
331 nsPrintfCString
error(
332 "Uniform `%s` is not linkable between"
333 " attached shaders.",
334 itrFrag
.name
.c_str());
343 for (const auto& fragVar
: mInterfaceBlocks
) {
344 for (const auto& vertVar
: vert
.mInterfaceBlocks
) {
345 if (vertVar
.name
!= fragVar
.name
) continue;
347 if (!vertVar
.isSameInterfaceBlockAtLinkTime(fragVar
)) {
348 nsPrintfCString
error(
349 "Interface block `%s` is not linkable between"
350 " attached shaders.",
351 fragVar
.name
.c_str());
361 std::vector
<sh::ShaderVariable
> staticUseVaryingList
;
363 for (const auto& fragVarying
: mVaryings
) {
364 static const char prefix
[] = "gl_";
365 if (StartsWith(fragVarying
.name
, prefix
)) {
366 if (fragVarying
.staticUse
) {
367 staticUseVaryingList
.push_back(fragVarying
);
372 bool definedInVertShader
= false;
373 bool staticVertUse
= false;
375 for (const auto& vertVarying
: vert
.mVaryings
) {
376 if (vertVarying
.name
!= fragVarying
.name
) continue;
378 if (!vertVarying
.isSameVaryingAtLinkTime(fragVarying
, mShaderVersion
)) {
379 nsPrintfCString
error(
380 "Varying `%s`is not linkable between"
381 " attached shaders.",
382 fragVarying
.name
.c_str());
387 definedInVertShader
= true;
388 staticVertUse
= vertVarying
.staticUse
;
392 if (!definedInVertShader
&& fragVarying
.staticUse
) {
393 nsPrintfCString
error(
394 "Varying `%s` has static-use in the frag"
395 " shader, but is undeclared in the vert"
397 fragVarying
.name
.c_str());
402 if (staticVertUse
&& fragVarying
.staticUse
) {
403 staticUseVaryingList
.push_back(fragVarying
);
407 if (!sh::CheckVariablesWithinPackingLimits(mMaxVaryingVectors
,
408 staticUseVaryingList
)) {
410 "Statically used varyings do not fit within packing limits. (see"
411 " GLSL ES Specification 1.0.17, p111)";
416 if (mShaderVersion
== 100) {
417 // Enforce ESSL1 invariant linking rules.
418 bool isInvariant_Position
= false;
419 bool isInvariant_PointSize
= false;
420 bool isInvariant_FragCoord
= false;
421 bool isInvariant_PointCoord
= false;
423 for (const auto& varying
: vert
.mVaryings
) {
424 if (varying
.name
== "gl_Position") {
425 isInvariant_Position
= varying
.isInvariant
;
426 } else if (varying
.name
== "gl_PointSize") {
427 isInvariant_PointSize
= varying
.isInvariant
;
431 for (const auto& varying
: mVaryings
) {
432 if (varying
.name
== "gl_FragCoord") {
433 isInvariant_FragCoord
= varying
.isInvariant
;
434 } else if (varying
.name
== "gl_PointCoord") {
435 isInvariant_PointCoord
= varying
.isInvariant
;
441 const auto fnCanBuiltInsLink
= [](bool vertIsInvariant
,
442 bool fragIsInvariant
) {
443 if (vertIsInvariant
) return true;
445 return !fragIsInvariant
;
448 if (!fnCanBuiltInsLink(isInvariant_Position
, isInvariant_FragCoord
)) {
450 "gl_Position must be invariant if gl_FragCoord is. (see GLSL ES"
451 " Specification 1.0.17, p39)";
455 if (!fnCanBuiltInsLink(isInvariant_PointSize
, isInvariant_PointCoord
)) {
457 "gl_PointSize must be invariant if gl_PointCoord is. (see GLSL ES"
458 " Specification 1.0.17, p39)";
466 size_t ShaderValidatorResults::SizeOfIncludingThis(
467 const MallocSizeOf fnSizeOf
) const {
468 auto ret
= fnSizeOf(this);
469 ret
+= mInfoLog
.size();
470 ret
+= mObjectCode
.size();
472 for (const auto& cur
: mAttributes
) {
473 ret
+= fnSizeOf(&cur
);
475 for (const auto& cur
: mInterfaceBlocks
) {
476 ret
+= fnSizeOf(&cur
);
478 for (const auto& cur
: mOutputVariables
) {
479 ret
+= fnSizeOf(&cur
);
481 for (const auto& cur
: mUniforms
) {
482 ret
+= fnSizeOf(&cur
);
484 for (const auto& cur
: mVaryings
) {
485 ret
+= fnSizeOf(&cur
);
492 } // namespace mozilla