Bug 1936278 - Prevent search mode chiclet from being dismissed when clicking in page...
[gecko.git] / dom / canvas / WebGLShaderValidator.cpp
blob6f8c679956bf8ca3e07a3155a9688185047ed12e
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 = {};
32 options.variables = true;
33 options.enforcePackingRestrictions = true;
34 options.objectCode = true;
35 options.initGLPosition = true;
36 options.initializeUninitializedLocals = true;
37 options.initOutputVariables = true;
39 #ifdef XP_MACOSX
40 options.removeInvariantAndCentroidForESSL3 = true;
41 #else
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;
46 #endif
48 if (gl->WorkAroundDriverBugs()) {
49 if (kIsMacOS) {
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
61 // incorrectly.
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;
75 // -
77 if (resources.MaxExpressionComplexity > 0) {
78 options.limitExpressionComplexity = true;
80 if (resources.MaxCallStackDepth > 0) {
81 options.limitCallStackDepth = true;
84 return options;
87 } // namespace webgl
89 ////////////////////////////////////////
91 static ShShaderOutput ShaderOutput(gl::GLContext* gl) {
92 if (gl->IsGLES()) {
93 return SH_ESSL_OUTPUT;
95 uint32_t version = gl->ShadingLanguageVersion();
96 switch (version) {
97 case 100:
98 return SH_GLSL_COMPATIBILITY_OUTPUT;
99 case 120:
100 return SH_GLSL_COMPATIBILITY_OUTPUT;
101 case 130:
102 return SH_GLSL_130_OUTPUT;
103 case 140:
104 return SH_GLSL_140_OUTPUT;
105 case 150:
106 return SH_GLSL_150_CORE_OUTPUT;
107 case 330:
108 return SH_GLSL_330_CORE_OUTPUT;
109 case 400:
110 return SH_GLSL_400_CORE_OUTPUT;
111 case 410:
112 return SH_GLSL_410_CORE_OUTPUT;
113 case 420:
114 return SH_GLSL_420_CORE_OUTPUT;
115 case 430:
116 return SH_GLSL_430_CORE_OUTPUT;
117 case 440:
118 return SH_GLSL_440_CORE_OUTPUT;
119 default:
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 sh::InitBuiltInResources(&resources);
139 resources.HashFunction = webgl::IdentifierHashFunc;
141 const auto& limits = Limits();
143 resources.MaxVertexAttribs = limits.maxVertexAttribs;
144 resources.MaxVertexUniformVectors = mGLMaxVertexUniformVectors;
145 resources.MaxVertexTextureImageUnits = mGLMaxVertexTextureImageUnits;
146 resources.MaxCombinedTextureImageUnits = limits.maxTexUnits;
147 resources.MaxTextureImageUnits = mGLMaxFragmentTextureImageUnits;
148 resources.MaxFragmentUniformVectors = mGLMaxFragmentUniformVectors;
150 resources.MaxVertexOutputVectors = mGLMaxVertexOutputVectors;
151 resources.MaxFragmentInputVectors = mGLMaxFragmentInputVectors;
152 resources.MaxVaryingVectors = mGLMaxFragmentInputVectors;
154 if (IsWebGL2()) {
155 resources.MinProgramTexelOffset = mGLMinProgramTexelOffset;
156 resources.MaxProgramTexelOffset = mGLMaxProgramTexelOffset;
159 resources.MaxDrawBuffers = MaxValidDrawBuffers();
161 if (IsExtensionEnabled(WebGLExtensionID::EXT_frag_depth))
162 resources.EXT_frag_depth = 1;
164 if (IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives))
165 resources.OES_standard_derivatives = 1;
167 if (IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers))
168 resources.EXT_draw_buffers = 1;
170 if (IsExtensionEnabled(WebGLExtensionID::EXT_shader_texture_lod))
171 resources.EXT_shader_texture_lod = 1;
173 if (IsExtensionEnabled(WebGLExtensionID::OVR_multiview2)) {
174 resources.OVR_multiview = 1;
175 resources.OVR_multiview2 = 1;
176 resources.MaxViewsOVR = limits.maxMultiviewLayers;
179 // Tell ANGLE to allow highp in frag shaders. (unless disabled)
180 // If underlying GLES doesn't have highp in frag shaders, it should complain
181 // anyways.
182 resources.FragmentPrecisionHigh = mDisableFragHighP ? 0 : 1;
184 if (gl->WorkAroundDriverBugs()) {
185 #ifdef XP_MACOSX
186 if (gl->Vendor() == gl::GLVendor::NVIDIA) {
187 // Work around bug 890432
188 resources.MaxExpressionComplexity = 1000;
190 #endif
193 // -
195 resources.MaxVariableSizeInBytes = [&]() -> size_t {
196 const auto kibytes = StaticPrefs::webgl_glsl_max_var_size_in_kibytes();
197 if (kibytes >= 0) {
198 return static_cast<size_t>(kibytes) * 1024;
201 return resources.MaxVariableSizeInBytes;
202 }();
204 resources.MaxPrivateVariableSizeInBytes = [&]() -> size_t {
205 const auto bytes = StaticPrefs::webgl_glsl_max_private_var_size_in_bytes();
206 if (bytes >= 0) {
207 return static_cast<size_t>(bytes);
210 if (kIsMacOS) {
211 return 128 * 1024; // 8k vec4s
214 return resources.MaxPrivateVariableSizeInBytes;
215 }();
217 // -
219 const auto compileOptions =
220 webgl::ChooseValidatorCompileOptions(resources, gl);
221 auto ret = webgl::ShaderValidator::Create(shaderType, spec, outputLanguage,
222 resources, compileOptions);
223 if (!ret) return ret;
225 ret->mIfNeeded_webgl_gl_VertexID_Offset |=
226 mBug_DrawArraysInstancedUserAttribFetchAffectedByFirst;
228 return ret;
231 ////////////////////////////////////////
233 namespace webgl {
235 /*static*/
236 std::unique_ptr<ShaderValidator> ShaderValidator::Create(
237 GLenum shaderType, ShShaderSpec spec, ShShaderOutput outputLanguage,
238 const ShBuiltInResources& resources, ShCompileOptions compileOptions) {
239 ShHandle handle =
240 sh::ConstructCompiler(shaderType, spec, outputLanguage, &resources);
241 MOZ_RELEASE_ASSERT(handle);
242 if (!handle) return nullptr;
244 return std::unique_ptr<ShaderValidator>(
245 new ShaderValidator(handle, compileOptions, resources.MaxVaryingVectors));
248 ShaderValidator::~ShaderValidator() { sh::Destruct(mHandle); }
250 inline bool StartsWith(const std::string_view& str,
251 const std::string_view& part) {
252 return str.find(part) == 0;
255 inline std::vector<std::string_view> Split(std::string_view src,
256 const std::string_view& delim,
257 const size_t maxSplits = -1) {
258 std::vector<std::string_view> ret;
259 for (const auto i : IntegerRange(maxSplits)) {
260 (void)i;
261 const auto end = src.find(delim);
262 if (end == size_t(-1)) {
263 break;
265 ret.push_back(src.substr(0, end));
266 src = src.substr(end + delim.size());
268 ret.push_back(src);
269 return ret;
272 std::unique_ptr<const ShaderValidatorResults>
273 ShaderValidator::ValidateAndTranslate(const char* const source) const {
274 auto ret = std::make_unique<ShaderValidatorResults>();
276 const std::array<const char*, 1> parts = {source};
277 ret->mValid =
278 sh::Compile(mHandle, parts.data(), parts.size(), mCompileOptions);
280 ret->mInfoLog = sh::GetInfoLog(mHandle);
282 if (ret->mValid) {
283 ret->mObjectCode = sh::GetObjectCode(mHandle);
284 ret->mShaderVersion = sh::GetShaderVersion(mHandle);
285 ret->mVertexShaderNumViews = sh::GetVertexShaderNumViews(mHandle);
287 ret->mAttributes = *sh::GetAttributes(mHandle);
288 ret->mInterfaceBlocks = *sh::GetInterfaceBlocks(mHandle);
289 ret->mOutputVariables = *sh::GetOutputVariables(mHandle);
290 ret->mUniforms = *sh::GetUniforms(mHandle);
291 ret->mVaryings = *sh::GetVaryings(mHandle);
293 ret->mMaxVaryingVectors = mMaxVaryingVectors;
295 const auto& nameMap = *sh::GetNameHashingMap(mHandle);
296 for (const auto& pair : nameMap) {
297 ret->mNameMap.insert(pair);
300 // -
301 // Custom translation steps
302 auto* const translatedSource = &ret->mObjectCode;
304 // gl_VertexID -> webgl_gl_VertexID
305 // gl_InstanceID -> webgl_gl_InstanceID
307 std::string header;
308 std::string_view body = *translatedSource;
309 if (StartsWith(body, "#version")) {
310 const auto parts = Split(body, "\n", 1);
311 header = parts.at(0);
312 header += "\n";
313 body = parts.at(1);
316 for (const auto& attrib : ret->mAttributes) {
317 if (mIfNeeded_webgl_gl_VertexID_Offset && attrib.name == "gl_VertexID" &&
318 attrib.staticUse) {
319 header += "uniform int webgl_gl_VertexID_Offset;\n";
320 header +=
321 "#define gl_VertexID (gl_VertexID + webgl_gl_VertexID_Offset)\n";
322 ret->mNeeds_webgl_gl_VertexID_Offset = true;
326 if (header.size()) {
327 auto combined = header;
328 combined += body;
329 *translatedSource = combined;
333 sh::ClearResults(mHandle);
334 return ret;
337 bool ShaderValidatorResults::CanLinkTo(const ShaderValidatorResults& vert,
338 nsCString* const out_log) const {
339 MOZ_ASSERT(mValid);
340 MOZ_ASSERT(vert.mValid);
342 if (vert.mShaderVersion != mShaderVersion) {
343 nsPrintfCString error(
344 "Vertex shader version %d does not match"
345 " fragment shader version %d.",
346 vert.mShaderVersion, mShaderVersion);
347 *out_log = error;
348 return false;
351 for (const auto& itrFrag : mUniforms) {
352 for (const auto& itrVert : vert.mUniforms) {
353 if (itrVert.name != itrFrag.name) continue;
355 if (!itrVert.isSameUniformAtLinkTime(itrFrag)) {
356 nsPrintfCString error(
357 "Uniform `%s` is not linkable between"
358 " attached shaders.",
359 itrFrag.name.c_str());
360 *out_log = error;
361 return false;
364 break;
368 for (const auto& fragVar : mInterfaceBlocks) {
369 for (const auto& vertVar : vert.mInterfaceBlocks) {
370 if (vertVar.name != fragVar.name) continue;
372 if (!vertVar.isSameInterfaceBlockAtLinkTime(fragVar)) {
373 nsPrintfCString error(
374 "Interface block `%s` is not linkable between"
375 " attached shaders.",
376 fragVar.name.c_str());
377 *out_log = error;
378 return false;
381 break;
386 std::vector<sh::ShaderVariable> staticUseVaryingList;
388 for (const auto& fragVarying : mVaryings) {
389 static const char prefix[] = "gl_";
390 if (StartsWith(fragVarying.name, prefix)) {
391 if (fragVarying.staticUse) {
392 staticUseVaryingList.push_back(fragVarying);
394 continue;
397 bool definedInVertShader = false;
398 bool staticVertUse = false;
400 for (const auto& vertVarying : vert.mVaryings) {
401 if (vertVarying.name != fragVarying.name) continue;
403 if (!vertVarying.isSameVaryingAtLinkTime(fragVarying, mShaderVersion)) {
404 nsPrintfCString error(
405 "Varying `%s`is not linkable between"
406 " attached shaders.",
407 fragVarying.name.c_str());
408 *out_log = error;
409 return false;
412 definedInVertShader = true;
413 staticVertUse = vertVarying.staticUse;
414 break;
417 if (!definedInVertShader && fragVarying.staticUse) {
418 nsPrintfCString error(
419 "Varying `%s` has static-use in the frag"
420 " shader, but is undeclared in the vert"
421 " shader.",
422 fragVarying.name.c_str());
423 *out_log = error;
424 return false;
427 if (staticVertUse && fragVarying.staticUse) {
428 staticUseVaryingList.push_back(fragVarying);
432 if (!sh::CheckVariablesWithinPackingLimits(mMaxVaryingVectors,
433 staticUseVaryingList)) {
434 *out_log =
435 "Statically used varyings do not fit within packing limits. (see"
436 " GLSL ES Specification 1.0.17, p111)";
437 return false;
441 if (mShaderVersion == 100) {
442 // Enforce ESSL1 invariant linking rules.
443 bool isInvariant_Position = false;
444 bool isInvariant_PointSize = false;
445 bool isInvariant_FragCoord = false;
446 bool isInvariant_PointCoord = false;
448 for (const auto& varying : vert.mVaryings) {
449 if (varying.name == "gl_Position") {
450 isInvariant_Position = varying.isInvariant;
451 } else if (varying.name == "gl_PointSize") {
452 isInvariant_PointSize = varying.isInvariant;
456 for (const auto& varying : mVaryings) {
457 if (varying.name == "gl_FragCoord") {
458 isInvariant_FragCoord = varying.isInvariant;
459 } else if (varying.name == "gl_PointCoord") {
460 isInvariant_PointCoord = varying.isInvariant;
464 ////
466 const auto fnCanBuiltInsLink = [](bool vertIsInvariant,
467 bool fragIsInvariant) {
468 if (vertIsInvariant) return true;
470 return !fragIsInvariant;
473 if (!fnCanBuiltInsLink(isInvariant_Position, isInvariant_FragCoord)) {
474 *out_log =
475 "gl_Position must be invariant if gl_FragCoord is. (see GLSL ES"
476 " Specification 1.0.17, p39)";
477 return false;
480 if (!fnCanBuiltInsLink(isInvariant_PointSize, isInvariant_PointCoord)) {
481 *out_log =
482 "gl_PointSize must be invariant if gl_PointCoord is. (see GLSL ES"
483 " Specification 1.0.17, p39)";
484 return false;
488 return true;
491 size_t ShaderValidatorResults::SizeOfIncludingThis(
492 const MallocSizeOf fnSizeOf) const {
493 auto ret = fnSizeOf(this);
494 ret += mInfoLog.size();
495 ret += mObjectCode.size();
497 for (const auto& cur : mAttributes) {
498 ret += fnSizeOf(&cur);
500 for (const auto& cur : mInterfaceBlocks) {
501 ret += fnSizeOf(&cur);
503 for (const auto& cur : mOutputVariables) {
504 ret += fnSizeOf(&cur);
506 for (const auto& cur : mUniforms) {
507 ret += fnSizeOf(&cur);
509 for (const auto& cur : mVaryings) {
510 ret += fnSizeOf(&cur);
513 return ret;
516 } // namespace webgl
517 } // namespace mozilla