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/. */
5
6 #include "WebGLShaderValidator.h"
7
8 #include "GLContext.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"
14 #include <string>
15 #include <vector>
16 #include "WebGLContext.h"
17
18 namespace mozilla {
19 namespace webgl {
20
IdentifierHashFunc(const char * name,size_t len)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.
24 uint64_t hash[2];
25 MurmurHash3_x86_128(name, len, 0, hash);
26 return hash[0];
27 }
28
ChooseValidatorCompileOptions(const ShBuiltInResources & resources,const mozilla::gl::GLContext * gl)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;
35
36 #ifdef XP_MACOSX
37 options |= SH_REMOVE_INVARIANT_AND_CENTROID_FOR_ESSL3;
38 #else
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;
43 #endif
44
45 if (gl->WorkAroundDriverBugs()) {
46 #ifdef XP_MACOSX
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;
50
51 // Work around that Mac drivers handle struct scopes incorrectly.
52 options |= SH_REGENERATE_STRUCT_NAMES;
53 options |= SH_INIT_OUTPUT_VARIABLES;
54
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;
58
59 options |= SH_REWRITE_TEXELFETCHOFFSET_TO_TEXELFETCH;
60 }
61 #endif
62
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;
67 }
68 }
69
70 if (StaticPrefs::webgl_all_angle_options()) {
71 options = -1;
72
73 options ^= SH_INTERMEDIATE_TREE;
74 options ^= SH_LINE_DIRECTIVES;
75 options ^= SH_SOURCE_PATH;
76
77 options ^= SH_LIMIT_EXPRESSION_COMPLEXITY;
78 options ^= SH_LIMIT_CALL_STACK_DEPTH;
79
80 options ^= SH_EXPAND_SELECT_HLSL_INTEGER_POW_EXPRESSIONS;
81 options ^= SH_HLSL_GET_DIMENSIONS_IGNORES_BASE_LEVEL;
82
83 options ^= SH_REMOVE_INVARIANT_AND_CENTROID_FOR_ESSL3;
84 }
85
86 if (resources.MaxExpressionComplexity > 0) {
87 options |= SH_LIMIT_EXPRESSION_COMPLEXITY;
88 }
89 if (resources.MaxCallStackDepth > 0) {
90 options |= SH_LIMIT_CALL_STACK_DEPTH;
91 }
92
93 return options;
94 }
95
96 } // namespace webgl
97
98 ////////////////////////////////////////
99
ShaderOutput(gl::GLContext * gl)100 static ShShaderOutput ShaderOutput(gl::GLContext* gl) {
101 if (gl->IsGLES()) {
102 return SH_ESSL_OUTPUT;
103 }
104 uint32_t version = gl->ShadingLanguageVersion();
105 switch (version) {
106 case 100:
107 return SH_GLSL_COMPATIBILITY_OUTPUT;
108 case 120:
109 return SH_GLSL_COMPATIBILITY_OUTPUT;
110 case 130:
111 return SH_GLSL_130_OUTPUT;
112 case 140:
113 return SH_GLSL_140_OUTPUT;
114 case 150:
115 return SH_GLSL_150_CORE_OUTPUT;
116 case 330:
117 return SH_GLSL_330_CORE_OUTPUT;
118 case 400:
119 return SH_GLSL_400_CORE_OUTPUT;
120 case 410:
121 return SH_GLSL_410_CORE_OUTPUT;
122 case 420:
123 return SH_GLSL_420_CORE_OUTPUT;
124 case 430:
125 return SH_GLSL_430_CORE_OUTPUT;
126 case 440:
127 return SH_GLSL_440_CORE_OUTPUT;
128 default:
129 if (version >= 450) {
130 // "OpenGL 4.6 is also guaranteed to support all previous versions of
131 // the OpenGL Shading Language back to version 1.10."
132 return SH_GLSL_450_CORE_OUTPUT;
133 }
134 gfxCriticalNote << "Unexpected GLSL version: " << version;
135 }
136
137 return SH_GLSL_COMPATIBILITY_OUTPUT;
138 }
139
CreateShaderValidator(GLenum shaderType) const140 std::unique_ptr<webgl::ShaderValidator> WebGLContext::CreateShaderValidator(
141 GLenum shaderType) const {
142 const auto spec = (IsWebGL2() ? SH_WEBGL2_SPEC : SH_WEBGL_SPEC);
143 const auto outputLanguage = ShaderOutput(gl);
144
145 ShBuiltInResources resources;
146 memset(&resources, 0, sizeof(resources));
147 sh::InitBuiltInResources(&resources);
148
149 resources.HashFunction = webgl::IdentifierHashFunc;
150
151 const auto& limits = Limits();
152
153 resources.MaxVertexAttribs = limits.maxVertexAttribs;
154 resources.MaxVertexUniformVectors = mGLMaxVertexUniformVectors;
155 resources.MaxVertexTextureImageUnits = mGLMaxVertexTextureImageUnits;
156 resources.MaxCombinedTextureImageUnits = limits.maxTexUnits;
157 resources.MaxTextureImageUnits = mGLMaxFragmentTextureImageUnits;
158 resources.MaxFragmentUniformVectors = mGLMaxFragmentUniformVectors;
159
160 resources.MaxVertexOutputVectors = mGLMaxVertexOutputVectors;
161 resources.MaxFragmentInputVectors = mGLMaxFragmentInputVectors;
162 resources.MaxVaryingVectors = mGLMaxFragmentInputVectors;
163
164 if (IsWebGL2()) {
165 resources.MinProgramTexelOffset = mGLMinProgramTexelOffset;
166 resources.MaxProgramTexelOffset = mGLMaxProgramTexelOffset;
167 }
168
169 resources.MaxDrawBuffers = MaxValidDrawBuffers();
170
171 if (IsExtensionEnabled(WebGLExtensionID::EXT_frag_depth))
172 resources.EXT_frag_depth = 1;
173
174 if (IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives))
175 resources.OES_standard_derivatives = 1;
176
177 if (IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers))
178 resources.EXT_draw_buffers = 1;
179
180 if (IsExtensionEnabled(WebGLExtensionID::EXT_shader_texture_lod))
181 resources.EXT_shader_texture_lod = 1;
182
183 if (IsExtensionEnabled(WebGLExtensionID::OVR_multiview2)) {
184 resources.OVR_multiview = 1;
185 resources.OVR_multiview2 = 1;
186 resources.MaxViewsOVR = limits.maxMultiviewLayers;
187 }
188
189 // Tell ANGLE to allow highp in frag shaders. (unless disabled)
190 // If underlying GLES doesn't have highp in frag shaders, it should complain
191 // anyways.
192 resources.FragmentPrecisionHigh = mDisableFragHighP ? 0 : 1;
193
194 if (gl->WorkAroundDriverBugs()) {
195 #ifdef XP_MACOSX
196 if (gl->Vendor() == gl::GLVendor::NVIDIA) {
197 // Work around bug 890432
198 resources.MaxExpressionComplexity = 1000;
199 }
200 #endif
201 }
202
203 const auto compileOptions =
204 webgl::ChooseValidatorCompileOptions(resources, gl);
205 return webgl::ShaderValidator::Create(shaderType, spec, outputLanguage,
206 resources, compileOptions);
207 }
208
209 ////////////////////////////////////////
210
211 namespace webgl {
212
213 /*static*/
Create(GLenum shaderType,ShShaderSpec spec,ShShaderOutput outputLanguage,const ShBuiltInResources & resources,ShCompileOptions compileOptions)214 std::unique_ptr<ShaderValidator> ShaderValidator::Create(
215 GLenum shaderType, ShShaderSpec spec, ShShaderOutput outputLanguage,
216 const ShBuiltInResources& resources, ShCompileOptions compileOptions) {
217 ShHandle handle =
218 sh::ConstructCompiler(shaderType, spec, outputLanguage, &resources);
219 MOZ_RELEASE_ASSERT(handle);
220 if (!handle) return nullptr;
221
222 return std::unique_ptr<ShaderValidator>(
223 new ShaderValidator(handle, compileOptions, resources.MaxVaryingVectors));
224 }
225
~ShaderValidator()226 ShaderValidator::~ShaderValidator() { sh::Destruct(mHandle); }
227
228 std::unique_ptr<const ShaderValidatorResults>
ValidateAndTranslate(const char * const source) const229 ShaderValidator::ValidateAndTranslate(const char* const source) const {
230 auto ret = std::make_unique<ShaderValidatorResults>();
231
232 const std::array<const char*, 1> parts = {source};
233 ret->mValid =
234 sh::Compile(mHandle, parts.data(), parts.size(), mCompileOptions);
235
236 ret->mInfoLog = sh::GetInfoLog(mHandle);
237
238 if (ret->mValid) {
239 ret->mObjectCode = sh::GetObjectCode(mHandle);
240 ret->mShaderVersion = sh::GetShaderVersion(mHandle);
241 ret->mVertexShaderNumViews = sh::GetVertexShaderNumViews(mHandle);
242
243 ret->mAttributes = *sh::GetAttributes(mHandle);
244 ret->mInterfaceBlocks = *sh::GetInterfaceBlocks(mHandle);
245 ret->mOutputVariables = *sh::GetOutputVariables(mHandle);
246 ret->mUniforms = *sh::GetUniforms(mHandle);
247 ret->mVaryings = *sh::GetVaryings(mHandle);
248
249 ret->mMaxVaryingVectors = mMaxVaryingVectors;
250
251 const auto& nameMap = *sh::GetNameHashingMap(mHandle);
252 for (const auto& pair : nameMap) {
253 ret->mNameMap.insert(pair);
254 }
255 }
256
257 sh::ClearResults(mHandle);
258 return ret;
259 }
260
261 template <size_t N>
StartsWith(const std::string & haystack,const char (& needle)[N])262 static bool StartsWith(const std::string& haystack, const char (&needle)[N]) {
263 return haystack.compare(0, N - 1, needle) == 0;
264 }
265
CanLinkTo(const ShaderValidatorResults & vert,nsCString * const out_log) const266 bool ShaderValidatorResults::CanLinkTo(const ShaderValidatorResults& vert,
267 nsCString* const out_log) const {
268 MOZ_ASSERT(mValid);
269 MOZ_ASSERT(vert.mValid);
270
271 if (vert.mShaderVersion != mShaderVersion) {
272 nsPrintfCString error(
273 "Vertex shader version %d does not match"
274 " fragment shader version %d.",
275 vert.mShaderVersion, mShaderVersion);
276 *out_log = error;
277 return false;
278 }
279
280 for (const auto& itrFrag : mUniforms) {
281 for (const auto& itrVert : vert.mUniforms) {
282 if (itrVert.name != itrFrag.name) continue;
283
284 if (!itrVert.isSameUniformAtLinkTime(itrFrag)) {
285 nsPrintfCString error(
286 "Uniform `%s` is not linkable between"
287 " attached shaders.",
288 itrFrag.name.c_str());
289 *out_log = error;
290 return false;
291 }
292
293 break;
294 }
295 }
296
297 for (const auto& fragVar : mInterfaceBlocks) {
298 for (const auto& vertVar : vert.mInterfaceBlocks) {
299 if (vertVar.name != fragVar.name) continue;
300
301 if (!vertVar.isSameInterfaceBlockAtLinkTime(fragVar)) {
302 nsPrintfCString error(
303 "Interface block `%s` is not linkable between"
304 " attached shaders.",
305 fragVar.name.c_str());
306 *out_log = error;
307 return false;
308 }
309
310 break;
311 }
312 }
313
314 {
315 std::vector<sh::ShaderVariable> staticUseVaryingList;
316
317 for (const auto& fragVarying : mVaryings) {
318 static const char prefix[] = "gl_";
319 if (StartsWith(fragVarying.name, prefix)) {
320 if (fragVarying.staticUse) {
321 staticUseVaryingList.push_back(fragVarying);
322 }
323 continue;
324 }
325
326 bool definedInVertShader = false;
327 bool staticVertUse = false;
328
329 for (const auto& vertVarying : vert.mVaryings) {
330 if (vertVarying.name != fragVarying.name) continue;
331
332 if (!vertVarying.isSameVaryingAtLinkTime(fragVarying, mShaderVersion)) {
333 nsPrintfCString error(
334 "Varying `%s`is not linkable between"
335 " attached shaders.",
336 fragVarying.name.c_str());
337 *out_log = error;
338 return false;
339 }
340
341 definedInVertShader = true;
342 staticVertUse = vertVarying.staticUse;
343 break;
344 }
345
346 if (!definedInVertShader && fragVarying.staticUse) {
347 nsPrintfCString error(
348 "Varying `%s` has static-use in the frag"
349 " shader, but is undeclared in the vert"
350 " shader.",
351 fragVarying.name.c_str());
352 *out_log = error;
353 return false;
354 }
355
356 if (staticVertUse && fragVarying.staticUse) {
357 staticUseVaryingList.push_back(fragVarying);
358 }
359 }
360
361 if (!sh::CheckVariablesWithinPackingLimits(mMaxVaryingVectors,
362 staticUseVaryingList)) {
363 *out_log =
364 "Statically used varyings do not fit within packing limits. (see"
365 " GLSL ES Specification 1.0.17, p111)";
366 return false;
367 }
368 }
369
370 if (mShaderVersion == 100) {
371 // Enforce ESSL1 invariant linking rules.
372 bool isInvariant_Position = false;
373 bool isInvariant_PointSize = false;
374 bool isInvariant_FragCoord = false;
375 bool isInvariant_PointCoord = false;
376
377 for (const auto& varying : vert.mVaryings) {
378 if (varying.name == "gl_Position") {
379 isInvariant_Position = varying.isInvariant;
380 } else if (varying.name == "gl_PointSize") {
381 isInvariant_PointSize = varying.isInvariant;
382 }
383 }
384
385 for (const auto& varying : mVaryings) {
386 if (varying.name == "gl_FragCoord") {
387 isInvariant_FragCoord = varying.isInvariant;
388 } else if (varying.name == "gl_PointCoord") {
389 isInvariant_PointCoord = varying.isInvariant;
390 }
391 }
392
393 ////
394
395 const auto fnCanBuiltInsLink = [](bool vertIsInvariant,
396 bool fragIsInvariant) {
397 if (vertIsInvariant) return true;
398
399 return !fragIsInvariant;
400 };
401
402 if (!fnCanBuiltInsLink(isInvariant_Position, isInvariant_FragCoord)) {
403 *out_log =
404 "gl_Position must be invariant if gl_FragCoord is. (see GLSL ES"
405 " Specification 1.0.17, p39)";
406 return false;
407 }
408
409 if (!fnCanBuiltInsLink(isInvariant_PointSize, isInvariant_PointCoord)) {
410 *out_log =
411 "gl_PointSize must be invariant if gl_PointCoord is. (see GLSL ES"
412 " Specification 1.0.17, p39)";
413 return false;
414 }
415 }
416
417 return true;
418 }
419
SizeOfIncludingThis(const MallocSizeOf fnSizeOf) const420 size_t ShaderValidatorResults::SizeOfIncludingThis(
421 const MallocSizeOf fnSizeOf) const {
422 auto ret = fnSizeOf(this);
423 ret += mInfoLog.size();
424 ret += mObjectCode.size();
425
426 for (const auto& cur : mAttributes) {
427 ret += fnSizeOf(&cur);
428 }
429 for (const auto& cur : mInterfaceBlocks) {
430 ret += fnSizeOf(&cur);
431 }
432 for (const auto& cur : mOutputVariables) {
433 ret += fnSizeOf(&cur);
434 }
435 for (const auto& cur : mUniforms) {
436 ret += fnSizeOf(&cur);
437 }
438 for (const auto& cur : mVaryings) {
439 ret += fnSizeOf(&cur);
440 }
441
442 return ret;
443 }
444
445 } // namespace webgl
446 } // namespace mozilla
447