Bug 1610775 [wpt PR 21336] - Update urllib3 to 1.25.8, a=testonly
[gecko.git] / dom / canvas / WebGLShaderValidator.cpp
blobc3c71285d33e0db29545462e852eb11587cddff2
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"
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"
18 namespace mozilla {
19 namespace webgl {
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];
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;
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
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;
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;
61 #endif
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()) {
71 options = -1;
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;
94 return options;
97 } // namespace webgl
99 ////////////////////////////////////////
101 static ShShaderOutput ShaderOutput(gl::GLContext* gl) {
102 if (gl->IsGLES()) {
103 return SH_ESSL_OUTPUT;
104 } else {
105 uint32_t version = gl->ShadingLanguageVersion();
106 switch (version) {
107 case 100:
108 return SH_GLSL_COMPATIBILITY_OUTPUT;
109 case 120:
110 return SH_GLSL_COMPATIBILITY_OUTPUT;
111 case 130:
112 return SH_GLSL_130_OUTPUT;
113 case 140:
114 return SH_GLSL_140_OUTPUT;
115 case 150:
116 return SH_GLSL_150_CORE_OUTPUT;
117 case 330:
118 return SH_GLSL_330_CORE_OUTPUT;
119 case 400:
120 return SH_GLSL_400_CORE_OUTPUT;
121 case 410:
122 return SH_GLSL_410_CORE_OUTPUT;
123 case 420:
124 return SH_GLSL_420_CORE_OUTPUT;
125 case 430:
126 return SH_GLSL_430_CORE_OUTPUT;
127 case 440:
128 return SH_GLSL_440_CORE_OUTPUT;
129 default:
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;
139 return SH_GLSL_COMPATIBILITY_OUTPUT;
142 std::unique_ptr<webgl::ShaderValidator> WebGLContext::CreateShaderValidator(
143 GLenum shaderType) const {
144 const auto spec = (IsWebGL2() ? SH_WEBGL2_SPEC : SH_WEBGL_SPEC);
145 const auto outputLanguage = ShaderOutput(gl);
147 ShBuiltInResources resources;
148 memset(&resources, 0, sizeof(resources));
149 sh::InitBuiltInResources(&resources);
151 resources.HashFunction = webgl::IdentifierHashFunc;
153 const auto& limits = Limits();
155 resources.MaxVertexAttribs = limits.maxVertexAttribs;
156 resources.MaxVertexUniformVectors = mGLMaxVertexUniformVectors;
157 resources.MaxVertexTextureImageUnits = mGLMaxVertexTextureImageUnits;
158 resources.MaxCombinedTextureImageUnits = limits.maxTexUnits;
159 resources.MaxTextureImageUnits = mGLMaxFragmentTextureImageUnits;
160 resources.MaxFragmentUniformVectors = mGLMaxFragmentUniformVectors;
162 resources.MaxVertexOutputVectors = mGLMaxVertexOutputVectors;
163 resources.MaxFragmentInputVectors = mGLMaxFragmentInputVectors;
164 resources.MaxVaryingVectors = mGLMaxFragmentInputVectors;
166 if (IsWebGL2()) {
167 resources.MinProgramTexelOffset = mGLMinProgramTexelOffset;
168 resources.MaxProgramTexelOffset = mGLMaxProgramTexelOffset;
171 resources.MaxDrawBuffers = MaxValidDrawBuffers();
173 if (IsExtensionEnabled(WebGLExtensionID::EXT_frag_depth))
174 resources.EXT_frag_depth = 1;
176 if (IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives))
177 resources.OES_standard_derivatives = 1;
179 if (IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers))
180 resources.EXT_draw_buffers = 1;
182 if (IsExtensionEnabled(WebGLExtensionID::EXT_shader_texture_lod))
183 resources.EXT_shader_texture_lod = 1;
185 if (IsExtensionEnabled(WebGLExtensionID::OVR_multiview2)) {
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
192 // anyways.
193 resources.FragmentPrecisionHigh = mDisableFragHighP ? 0 : 1;
195 if (gl->WorkAroundDriverBugs()) {
196 #ifdef XP_MACOSX
197 if (gl->Vendor() == gl::GLVendor::NVIDIA) {
198 // Work around bug 890432
199 resources.MaxExpressionComplexity = 1000;
201 #endif
204 const auto compileOptions =
205 webgl::ChooseValidatorCompileOptions(resources, gl);
206 return webgl::ShaderValidator::Create(shaderType, spec, outputLanguage,
207 resources, compileOptions);
210 ////////////////////////////////////////
212 namespace webgl {
214 /*static*/
215 std::unique_ptr<ShaderValidator> ShaderValidator::Create(
216 GLenum shaderType, ShShaderSpec spec, ShShaderOutput outputLanguage,
217 const ShBuiltInResources& resources, ShCompileOptions compileOptions) {
218 ShHandle handle =
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};
234 ret->mValid =
235 sh::Compile(mHandle, parts.data(), parts.size(), mCompileOptions);
237 ret->mInfoLog = sh::GetInfoLog(mHandle);
239 if (ret->mValid) {
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);
259 return ret;
262 template <size_t N>
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 {
269 MOZ_ASSERT(mValid);
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);
277 *out_log = error;
278 return false;
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());
290 *out_log = error;
291 return false;
294 break;
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());
307 *out_log = error;
308 return false;
311 break;
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);
324 continue;
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());
338 *out_log = error;
339 return false;
342 definedInVertShader = true;
343 staticVertUse = vertVarying.staticUse;
344 break;
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"
351 " shader.",
352 fragVarying.name.c_str());
353 *out_log = error;
354 return false;
357 if (staticVertUse && fragVarying.staticUse) {
358 staticUseVaryingList.push_back(fragVarying);
362 if (!sh::CheckVariablesWithinPackingLimits(mMaxVaryingVectors,
363 staticUseVaryingList)) {
364 *out_log =
365 "Statically used varyings do not fit within packing limits. (see"
366 " GLSL ES Specification 1.0.17, p111)";
367 return false;
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;
394 ////
396 const auto fnCanBuiltInsLink = [](bool vertIsInvariant,
397 bool fragIsInvariant) {
398 if (vertIsInvariant) return true;
400 return !fragIsInvariant;
403 if (!fnCanBuiltInsLink(isInvariant_Position, isInvariant_FragCoord)) {
404 *out_log =
405 "gl_Position must be invariant if gl_FragCoord is. (see GLSL ES"
406 " Specification 1.0.17, p39)";
407 return false;
410 if (!fnCanBuiltInsLink(isInvariant_PointSize, isInvariant_PointCoord)) {
411 *out_log =
412 "gl_PointSize must be invariant if gl_PointCoord is. (see GLSL ES"
413 " Specification 1.0.17, p39)";
414 return false;
418 return true;
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);
443 return ret;
446 } // namespace webgl
447 } // namespace mozilla