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
= SH_VARIABLES
| SH_ENFORCE_PACKING_RESTRICTIONS
|
32 SH_OBJECT_CODE
| SH_INIT_GL_POSITION
|
33 SH_INITIALIZE_UNINITIALIZED_LOCALS
|
34 SH_INIT_OUTPUT_VARIABLES
;
37 options
|= SH_REMOVE_INVARIANT_AND_CENTROID_FOR_ESSL3
;
39 // We want to do this everywhere, but to do this on Mac, we need
40 // to do it only on Mac OSX > 10.6 as this causes the shader
41 // compiler in 10.6 to crash
42 options
|= SH_CLAMP_INDIRECT_ARRAY_BOUNDS
;
45 if (gl
->WorkAroundDriverBugs()) {
47 // Work around https://bugs.webkit.org/show_bug.cgi?id=124684,
48 // https://chromium.googlesource.com/angle/angle/+/5e70cf9d0b1bb
49 options
|= SH_UNFOLD_SHORT_CIRCUIT
;
51 // Work around that Mac drivers handle struct scopes incorrectly.
52 options
|= SH_REGENERATE_STRUCT_NAMES
;
53 options
|= SH_INIT_OUTPUT_VARIABLES
;
55 if (gl
->Vendor() == gl::GLVendor::Intel
) {
56 // Work around that Intel drivers on Mac OSX handle for-loop incorrectly.
57 options
|= SH_ADD_AND_TRUE_TO_LOOP_CONDITION
;
59 options
|= SH_REWRITE_TEXELFETCHOFFSET_TO_TEXELFETCH
;
63 if (!gl
->IsANGLE() && gl
->Vendor() == gl::GLVendor::Intel
) {
64 // Failures on at least Windows+Intel+OGL on:
65 // conformance/glsl/constructors/glsl-construct-mat2.html
66 options
|= SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS
;
70 if (StaticPrefs::webgl_all_angle_options()) {
73 options
^= SH_INTERMEDIATE_TREE
;
74 options
^= SH_LINE_DIRECTIVES
;
75 options
^= SH_SOURCE_PATH
;
77 options
^= SH_LIMIT_EXPRESSION_COMPLEXITY
;
78 options
^= SH_LIMIT_CALL_STACK_DEPTH
;
80 options
^= SH_EXPAND_SELECT_HLSL_INTEGER_POW_EXPRESSIONS
;
81 options
^= SH_HLSL_GET_DIMENSIONS_IGNORES_BASE_LEVEL
;
83 options
^= SH_DONT_REMOVE_INVARIANT_FOR_FRAGMENT_INPUT
;
84 options
^= SH_REMOVE_INVARIANT_AND_CENTROID_FOR_ESSL3
;
87 if (resources
.MaxExpressionComplexity
> 0) {
88 options
|= SH_LIMIT_EXPRESSION_COMPLEXITY
;
90 if (resources
.MaxCallStackDepth
> 0) {
91 options
|= SH_LIMIT_CALL_STACK_DEPTH
;
99 ////////////////////////////////////////
101 static ShShaderOutput
ShaderOutput(gl::GLContext
* gl
) {
103 return SH_ESSL_OUTPUT
;
105 uint32_t version
= gl
->ShadingLanguageVersion();
108 return SH_GLSL_COMPATIBILITY_OUTPUT
;
110 return SH_GLSL_COMPATIBILITY_OUTPUT
;
112 return SH_GLSL_130_OUTPUT
;
114 return SH_GLSL_140_OUTPUT
;
116 return SH_GLSL_150_CORE_OUTPUT
;
118 return SH_GLSL_330_CORE_OUTPUT
;
120 return SH_GLSL_400_CORE_OUTPUT
;
122 return SH_GLSL_410_CORE_OUTPUT
;
124 return SH_GLSL_420_CORE_OUTPUT
;
126 return SH_GLSL_430_CORE_OUTPUT
;
128 return SH_GLSL_440_CORE_OUTPUT
;
130 if (version
>= 450) {
131 // "OpenGL 4.6 is also guaranteed to support all previous versions of
132 // the OpenGL Shading Language back to version 1.10."
133 return SH_GLSL_450_CORE_OUTPUT
;
135 gfxCriticalNote
<< "Unexpected GLSL version: " << version
;
138 return SH_GLSL_COMPATIBILITY_OUTPUT
;
141 std::unique_ptr
<webgl::ShaderValidator
> WebGLContext::CreateShaderValidator(
142 GLenum shaderType
) const {
143 const auto spec
= (IsWebGL2() ? SH_WEBGL2_SPEC
: SH_WEBGL_SPEC
);
144 const auto outputLanguage
= ShaderOutput(gl
);
146 ShBuiltInResources resources
;
147 memset(&resources
, 0, sizeof(resources
));
148 sh::InitBuiltInResources(&resources
);
150 resources
.HashFunction
= webgl::IdentifierHashFunc
;
152 const auto& limits
= Limits();
154 resources
.MaxVertexAttribs
= limits
.maxVertexAttribs
;
155 resources
.MaxVertexUniformVectors
= mGLMaxVertexUniformVectors
;
156 resources
.MaxVertexTextureImageUnits
= mGLMaxVertexTextureImageUnits
;
157 resources
.MaxCombinedTextureImageUnits
= limits
.maxTexUnits
;
158 resources
.MaxTextureImageUnits
= mGLMaxFragmentTextureImageUnits
;
159 resources
.MaxFragmentUniformVectors
= mGLMaxFragmentUniformVectors
;
161 resources
.MaxVertexOutputVectors
= mGLMaxVertexOutputVectors
;
162 resources
.MaxFragmentInputVectors
= mGLMaxFragmentInputVectors
;
163 resources
.MaxVaryingVectors
= mGLMaxFragmentInputVectors
;
166 resources
.MinProgramTexelOffset
= mGLMinProgramTexelOffset
;
167 resources
.MaxProgramTexelOffset
= mGLMaxProgramTexelOffset
;
170 resources
.MaxDrawBuffers
= MaxValidDrawBuffers();
172 if (IsExtensionEnabled(WebGLExtensionID::EXT_frag_depth
))
173 resources
.EXT_frag_depth
= 1;
175 if (IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives
))
176 resources
.OES_standard_derivatives
= 1;
178 if (IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers
))
179 resources
.EXT_draw_buffers
= 1;
181 if (IsExtensionEnabled(WebGLExtensionID::EXT_shader_texture_lod
))
182 resources
.EXT_shader_texture_lod
= 1;
184 if (IsExtensionEnabled(WebGLExtensionID::OVR_multiview2
)) {
185 resources
.OVR_multiview
= 1;
186 resources
.OVR_multiview2
= 1;
187 resources
.MaxViewsOVR
= limits
.maxMultiviewLayers
;
190 // Tell ANGLE to allow highp in frag shaders. (unless disabled)
191 // If underlying GLES doesn't have highp in frag shaders, it should complain
193 resources
.FragmentPrecisionHigh
= mDisableFragHighP
? 0 : 1;
195 if (gl
->WorkAroundDriverBugs()) {
197 if (gl
->Vendor() == gl::GLVendor::NVIDIA
) {
198 // Work around bug 890432
199 resources
.MaxExpressionComplexity
= 1000;
204 const auto compileOptions
=
205 webgl::ChooseValidatorCompileOptions(resources
, gl
);
206 return webgl::ShaderValidator::Create(shaderType
, spec
, outputLanguage
,
207 resources
, compileOptions
);
210 ////////////////////////////////////////
215 std::unique_ptr
<ShaderValidator
> ShaderValidator::Create(
216 GLenum shaderType
, ShShaderSpec spec
, ShShaderOutput outputLanguage
,
217 const ShBuiltInResources
& resources
, ShCompileOptions compileOptions
) {
219 sh::ConstructCompiler(shaderType
, spec
, outputLanguage
, &resources
);
220 MOZ_RELEASE_ASSERT(handle
);
221 if (!handle
) return nullptr;
223 return std::unique_ptr
<ShaderValidator
>(
224 new ShaderValidator(handle
, compileOptions
, resources
.MaxVaryingVectors
));
227 ShaderValidator::~ShaderValidator() { sh::Destruct(mHandle
); }
229 std::unique_ptr
<const ShaderValidatorResults
>
230 ShaderValidator::ValidateAndTranslate(const char* const source
) const {
231 auto ret
= std::make_unique
<ShaderValidatorResults
>();
233 const std::array
<const char*, 1> parts
= {source
};
235 sh::Compile(mHandle
, parts
.data(), parts
.size(), mCompileOptions
);
237 ret
->mInfoLog
= sh::GetInfoLog(mHandle
);
240 ret
->mObjectCode
= sh::GetObjectCode(mHandle
);
241 ret
->mShaderVersion
= sh::GetShaderVersion(mHandle
);
242 ret
->mVertexShaderNumViews
= sh::GetVertexShaderNumViews(mHandle
);
244 ret
->mAttributes
= *sh::GetAttributes(mHandle
);
245 ret
->mInterfaceBlocks
= *sh::GetInterfaceBlocks(mHandle
);
246 ret
->mOutputVariables
= *sh::GetOutputVariables(mHandle
);
247 ret
->mUniforms
= *sh::GetUniforms(mHandle
);
248 ret
->mVaryings
= *sh::GetVaryings(mHandle
);
250 ret
->mMaxVaryingVectors
= mMaxVaryingVectors
;
252 const auto& nameMap
= *sh::GetNameHashingMap(mHandle
);
253 for (const auto& pair
: nameMap
) {
254 ret
->mNameMap
.insert(pair
);
258 sh::ClearResults(mHandle
);
263 static bool StartsWith(const std::string
& haystack
, const char (&needle
)[N
]) {
264 return haystack
.compare(0, N
- 1, needle
) == 0;
267 bool ShaderValidatorResults::CanLinkTo(const ShaderValidatorResults
& vert
,
268 nsCString
* const out_log
) const {
270 MOZ_ASSERT(vert
.mValid
);
272 if (vert
.mShaderVersion
!= mShaderVersion
) {
273 nsPrintfCString
error(
274 "Vertex shader version %d does not match"
275 " fragment shader version %d.",
276 vert
.mShaderVersion
, mShaderVersion
);
281 for (const auto& itrFrag
: mUniforms
) {
282 for (const auto& itrVert
: vert
.mUniforms
) {
283 if (itrVert
.name
!= itrFrag
.name
) continue;
285 if (!itrVert
.isSameUniformAtLinkTime(itrFrag
)) {
286 nsPrintfCString
error(
287 "Uniform `%s` is not linkable between"
288 " attached shaders.",
289 itrFrag
.name
.c_str());
298 for (const auto& fragVar
: mInterfaceBlocks
) {
299 for (const auto& vertVar
: vert
.mInterfaceBlocks
) {
300 if (vertVar
.name
!= fragVar
.name
) continue;
302 if (!vertVar
.isSameInterfaceBlockAtLinkTime(fragVar
)) {
303 nsPrintfCString
error(
304 "Interface block `%s` is not linkable between"
305 " attached shaders.",
306 fragVar
.name
.c_str());
316 std::vector
<sh::ShaderVariable
> staticUseVaryingList
;
318 for (const auto& fragVarying
: mVaryings
) {
319 static const char prefix
[] = "gl_";
320 if (StartsWith(fragVarying
.name
, prefix
)) {
321 if (fragVarying
.staticUse
) {
322 staticUseVaryingList
.push_back(fragVarying
);
327 bool definedInVertShader
= false;
328 bool staticVertUse
= false;
330 for (const auto& vertVarying
: vert
.mVaryings
) {
331 if (vertVarying
.name
!= fragVarying
.name
) continue;
333 if (!vertVarying
.isSameVaryingAtLinkTime(fragVarying
, mShaderVersion
)) {
334 nsPrintfCString
error(
335 "Varying `%s`is not linkable between"
336 " attached shaders.",
337 fragVarying
.name
.c_str());
342 definedInVertShader
= true;
343 staticVertUse
= vertVarying
.staticUse
;
347 if (!definedInVertShader
&& fragVarying
.staticUse
) {
348 nsPrintfCString
error(
349 "Varying `%s` has static-use in the frag"
350 " shader, but is undeclared in the vert"
352 fragVarying
.name
.c_str());
357 if (staticVertUse
&& fragVarying
.staticUse
) {
358 staticUseVaryingList
.push_back(fragVarying
);
362 if (!sh::CheckVariablesWithinPackingLimits(mMaxVaryingVectors
,
363 staticUseVaryingList
)) {
365 "Statically used varyings do not fit within packing limits. (see"
366 " GLSL ES Specification 1.0.17, p111)";
371 if (mShaderVersion
== 100) {
372 // Enforce ESSL1 invariant linking rules.
373 bool isInvariant_Position
= false;
374 bool isInvariant_PointSize
= false;
375 bool isInvariant_FragCoord
= false;
376 bool isInvariant_PointCoord
= false;
378 for (const auto& varying
: vert
.mVaryings
) {
379 if (varying
.name
== "gl_Position") {
380 isInvariant_Position
= varying
.isInvariant
;
381 } else if (varying
.name
== "gl_PointSize") {
382 isInvariant_PointSize
= varying
.isInvariant
;
386 for (const auto& varying
: mVaryings
) {
387 if (varying
.name
== "gl_FragCoord") {
388 isInvariant_FragCoord
= varying
.isInvariant
;
389 } else if (varying
.name
== "gl_PointCoord") {
390 isInvariant_PointCoord
= varying
.isInvariant
;
396 const auto fnCanBuiltInsLink
= [](bool vertIsInvariant
,
397 bool fragIsInvariant
) {
398 if (vertIsInvariant
) return true;
400 return !fragIsInvariant
;
403 if (!fnCanBuiltInsLink(isInvariant_Position
, isInvariant_FragCoord
)) {
405 "gl_Position must be invariant if gl_FragCoord is. (see GLSL ES"
406 " Specification 1.0.17, p39)";
410 if (!fnCanBuiltInsLink(isInvariant_PointSize
, isInvariant_PointCoord
)) {
412 "gl_PointSize must be invariant if gl_PointCoord is. (see GLSL ES"
413 " Specification 1.0.17, p39)";
421 size_t ShaderValidatorResults::SizeOfIncludingThis(
422 const MallocSizeOf fnSizeOf
) const {
423 auto ret
= fnSizeOf(this);
424 ret
+= mInfoLog
.size();
425 ret
+= mObjectCode
.size();
427 for (const auto& cur
: mAttributes
) {
428 ret
+= fnSizeOf(&cur
);
430 for (const auto& cur
: mInterfaceBlocks
) {
431 ret
+= fnSizeOf(&cur
);
433 for (const auto& cur
: mOutputVariables
) {
434 ret
+= fnSizeOf(&cur
);
436 for (const auto& cur
: mUniforms
) {
437 ret
+= fnSizeOf(&cur
);
439 for (const auto& cur
: mVaryings
) {
440 ret
+= fnSizeOf(&cur
);
447 } // namespace mozilla