Bug 1892041 - Part 1: Update test262 features. r=spidermonkey-reviewers,dminor
[gecko.git] / dom / canvas / WebGLShaderValidator.cpp
blob0921da6e256cc9c814b9a2531826cb7cad26aa99
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 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;
155 if (IsWebGL2()) {
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
182 // anyways.
183 resources.FragmentPrecisionHigh = mDisableFragHighP ? 0 : 1;
185 if (gl->WorkAroundDriverBugs()) {
186 #ifdef XP_MACOSX
187 if (gl->Vendor() == gl::GLVendor::NVIDIA) {
188 // Work around bug 890432
189 resources.MaxExpressionComplexity = 1000;
191 #endif
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;
203 return ret;
206 ////////////////////////////////////////
208 namespace webgl {
210 /*static*/
211 std::unique_ptr<ShaderValidator> ShaderValidator::Create(
212 GLenum shaderType, ShShaderSpec spec, ShShaderOutput outputLanguage,
213 const ShBuiltInResources& resources, ShCompileOptions compileOptions) {
214 ShHandle handle =
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)) {
235 (void)i;
236 const auto end = src.find(delim);
237 if (end == size_t(-1)) {
238 break;
240 ret.push_back(src.substr(0, end));
241 src = src.substr(end + delim.size());
243 ret.push_back(src);
244 return ret;
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};
252 ret->mValid =
253 sh::Compile(mHandle, parts.data(), parts.size(), mCompileOptions);
255 ret->mInfoLog = sh::GetInfoLog(mHandle);
257 if (ret->mValid) {
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);
275 // -
276 // Custom translation steps
277 auto* const translatedSource = &ret->mObjectCode;
279 // gl_VertexID -> webgl_gl_VertexID
280 // gl_InstanceID -> webgl_gl_InstanceID
282 std::string header;
283 std::string_view body = *translatedSource;
284 if (StartsWith(body, "#version")) {
285 const auto parts = Split(body, "\n", 1);
286 header = parts.at(0);
287 header += "\n";
288 body = parts.at(1);
291 for (const auto& attrib : ret->mAttributes) {
292 if (mIfNeeded_webgl_gl_VertexID_Offset && attrib.name == "gl_VertexID" &&
293 attrib.staticUse) {
294 header += "uniform int webgl_gl_VertexID_Offset;\n";
295 header +=
296 "#define gl_VertexID (gl_VertexID + webgl_gl_VertexID_Offset)\n";
297 ret->mNeeds_webgl_gl_VertexID_Offset = true;
301 if (header.size()) {
302 auto combined = header;
303 combined += body;
304 *translatedSource = combined;
308 sh::ClearResults(mHandle);
309 return ret;
312 bool ShaderValidatorResults::CanLinkTo(const ShaderValidatorResults& vert,
313 nsCString* const out_log) const {
314 MOZ_ASSERT(mValid);
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);
322 *out_log = error;
323 return false;
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());
335 *out_log = error;
336 return false;
339 break;
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());
352 *out_log = error;
353 return false;
356 break;
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);
369 continue;
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());
383 *out_log = error;
384 return false;
387 definedInVertShader = true;
388 staticVertUse = vertVarying.staticUse;
389 break;
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"
396 " shader.",
397 fragVarying.name.c_str());
398 *out_log = error;
399 return false;
402 if (staticVertUse && fragVarying.staticUse) {
403 staticUseVaryingList.push_back(fragVarying);
407 if (!sh::CheckVariablesWithinPackingLimits(mMaxVaryingVectors,
408 staticUseVaryingList)) {
409 *out_log =
410 "Statically used varyings do not fit within packing limits. (see"
411 " GLSL ES Specification 1.0.17, p111)";
412 return false;
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;
439 ////
441 const auto fnCanBuiltInsLink = [](bool vertIsInvariant,
442 bool fragIsInvariant) {
443 if (vertIsInvariant) return true;
445 return !fragIsInvariant;
448 if (!fnCanBuiltInsLink(isInvariant_Position, isInvariant_FragCoord)) {
449 *out_log =
450 "gl_Position must be invariant if gl_FragCoord is. (see GLSL ES"
451 " Specification 1.0.17, p39)";
452 return false;
455 if (!fnCanBuiltInsLink(isInvariant_PointSize, isInvariant_PointCoord)) {
456 *out_log =
457 "gl_PointSize must be invariant if gl_PointCoord is. (see GLSL ES"
458 " Specification 1.0.17, p39)";
459 return false;
463 return true;
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);
488 return ret;
491 } // namespace webgl
492 } // namespace mozilla