tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

WebGLShaderValidator.cpp (15352B)


      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 <string>
      9 #include <vector>
     10 
     11 #include "GLContext.h"
     12 #include "MurmurHash3.h"
     13 #include "WebGLContext.h"
     14 #include "mozilla/Preferences.h"
     15 #include "mozilla/StaticPrefs_webgl.h"
     16 #include "mozilla/gfx/Logging.h"
     17 #include "nsPrintfCString.h"
     18 
     19 namespace mozilla {
     20 namespace webgl {
     21 
     22 uint64_t IdentifierHashFunc(const char* name, size_t len) {
     23  // NB: we use the x86 function everywhere, even though it's suboptimal perf
     24  // on x64.  They return different results; not sure if that's a requirement.
     25  uint64_t hash[2];
     26  MurmurHash3_x86_128(name, len, 0, hash);
     27  return hash[0];
     28 }
     29 
     30 static ShCompileOptions ChooseValidatorCompileOptions(
     31    const ShBuiltInResources& resources, const mozilla::gl::GLContext* gl) {
     32  ShCompileOptions options = {};
     33  options.variables = true;
     34  options.enforcePackingRestrictions = true;
     35  options.objectCode = true;
     36  options.initGLPosition = true;
     37  options.initializeUninitializedLocals = true;
     38  options.initOutputVariables = true;
     39  options.clampIndirectArrayBounds = true;
     40 
     41  if (kIsMacOS) {
     42    options.removeInvariantAndCentroidForESSL3 = true;
     43  }
     44 
     45  if (gl->WorkAroundDriverBugs()) {
     46    if (kIsMacOS) {
     47      // Work around https://bugs.webkit.org/show_bug.cgi?id=124684,
     48      // https://chromium.googlesource.com/angle/angle/+/5e70cf9d0b1bb
     49      options.unfoldShortCircuit = true;
     50 
     51      // Work around that Mac drivers handle struct scopes incorrectly.
     52      options.regenerateStructNames = true;
     53      options.initOutputVariables = true;
     54      options.initGLPointSize = true;
     55 
     56      if (gl->Vendor() == gl::GLVendor::Intel) {
     57        // Work around that Intel drivers on Mac OSX handle for-loop
     58        // incorrectly.
     59        options.addAndTrueToLoopCondition = true;
     60 
     61        options.rewriteTexelFetchOffsetToTexelFetch = true;
     62      }
     63    }
     64 
     65    if (!gl->IsANGLE() && gl->Vendor() == gl::GLVendor::Intel) {
     66      // Failures on at least Windows+Intel+OGL on:
     67      // conformance/glsl/constructors/glsl-construct-mat2.html
     68      options.scalarizeVecAndMatConstructorArgs = true;
     69    }
     70  }
     71 
     72  // -
     73 
     74  if (resources.MaxExpressionComplexity > 0) {
     75    options.limitExpressionComplexity = true;
     76  }
     77  if (resources.MaxCallStackDepth > 0) {
     78    options.limitCallStackDepth = true;
     79  }
     80 
     81  return options;
     82 }
     83 
     84 }  // namespace webgl
     85 
     86 ////////////////////////////////////////
     87 
     88 static ShShaderOutput ShaderOutput(gl::GLContext* gl) {
     89  if (gl->IsGLES()) {
     90    return SH_ESSL_OUTPUT;
     91  }
     92  uint32_t version = gl->ShadingLanguageVersion();
     93  switch (version) {
     94    case 100:
     95      return SH_GLSL_COMPATIBILITY_OUTPUT;
     96    case 120:
     97      return SH_GLSL_COMPATIBILITY_OUTPUT;
     98    case 130:
     99      return SH_GLSL_130_OUTPUT;
    100    case 140:
    101      return SH_GLSL_140_OUTPUT;
    102    case 150:
    103      return SH_GLSL_150_CORE_OUTPUT;
    104    case 330:
    105      return SH_GLSL_330_CORE_OUTPUT;
    106    case 400:
    107      return SH_GLSL_400_CORE_OUTPUT;
    108    case 410:
    109      return SH_GLSL_410_CORE_OUTPUT;
    110    case 420:
    111      return SH_GLSL_420_CORE_OUTPUT;
    112    case 430:
    113      return SH_GLSL_430_CORE_OUTPUT;
    114    case 440:
    115      return SH_GLSL_440_CORE_OUTPUT;
    116    default:
    117      if (version >= 450) {
    118        // "OpenGL 4.6 is also guaranteed to support all previous versions of
    119        // the OpenGL Shading Language back to version 1.10."
    120        return SH_GLSL_450_CORE_OUTPUT;
    121      }
    122      gfxCriticalNote << "Unexpected GLSL version: " << version;
    123  }
    124 
    125  return SH_GLSL_COMPATIBILITY_OUTPUT;
    126 }
    127 
    128 std::unique_ptr<webgl::ShaderValidator> WebGLContext::CreateShaderValidator(
    129    GLenum shaderType) const {
    130  const auto spec = (IsWebGL2() ? SH_WEBGL2_SPEC : SH_WEBGL_SPEC);
    131  const auto outputLanguage = ShaderOutput(gl);
    132 
    133  ShBuiltInResources resources;
    134  sh::InitBuiltInResources(&resources);
    135 
    136  resources.HashFunction = webgl::IdentifierHashFunc;
    137 
    138  const auto& limits = Limits();
    139 
    140  resources.MaxVertexAttribs = limits.maxVertexAttribs;
    141  resources.MaxVertexUniformVectors = mGLMaxVertexUniformVectors;
    142  resources.MaxVertexTextureImageUnits = mGLMaxVertexTextureImageUnits;
    143  resources.MaxCombinedTextureImageUnits = limits.maxTexUnits;
    144  resources.MaxTextureImageUnits = mGLMaxFragmentTextureImageUnits;
    145  resources.MaxFragmentUniformVectors = mGLMaxFragmentUniformVectors;
    146 
    147  resources.MaxVertexOutputVectors = mGLMaxVertexOutputVectors;
    148  resources.MaxFragmentInputVectors = mGLMaxFragmentInputVectors;
    149  resources.MaxVaryingVectors = mGLMaxFragmentInputVectors;
    150 
    151  if (IsWebGL2()) {
    152    resources.MinProgramTexelOffset = mGLMinProgramTexelOffset;
    153    resources.MaxProgramTexelOffset = mGLMaxProgramTexelOffset;
    154  }
    155 
    156  resources.MaxDrawBuffers = MaxValidDrawBuffers();
    157 
    158  if (IsExtensionEnabled(WebGLExtensionID::EXT_frag_depth))
    159    resources.EXT_frag_depth = 1;
    160 
    161  if (IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives))
    162    resources.OES_standard_derivatives = 1;
    163 
    164  if (IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers))
    165    resources.EXT_draw_buffers = 1;
    166 
    167  if (IsExtensionEnabled(WebGLExtensionID::EXT_shader_texture_lod))
    168    resources.EXT_shader_texture_lod = 1;
    169 
    170  if (IsExtensionEnabled(WebGLExtensionID::OVR_multiview2)) {
    171    resources.OVR_multiview = 1;
    172    resources.OVR_multiview2 = 1;
    173    resources.MaxViewsOVR = limits.maxMultiviewLayers;
    174  }
    175 
    176  // Tell ANGLE to allow highp in frag shaders. (unless disabled)
    177  // If underlying GLES doesn't have highp in frag shaders, it should complain
    178  // anyways.
    179  resources.FragmentPrecisionHigh = mDisableFragHighP ? 0 : 1;
    180 
    181  if (gl->WorkAroundDriverBugs()) {
    182 #ifdef XP_MACOSX
    183    if (gl->Vendor() == gl::GLVendor::NVIDIA) {
    184      // Work around bug 890432
    185      resources.MaxExpressionComplexity = 1000;
    186    }
    187 #endif
    188  }
    189 
    190  // -
    191 
    192  resources.MaxVariableSizeInBytes = [&]() -> size_t {
    193    const auto kibytes = StaticPrefs::webgl_glsl_max_var_size_in_kibytes();
    194    if (kibytes >= 0) {
    195      return static_cast<size_t>(kibytes) * 1024;
    196    }
    197 
    198    return resources.MaxVariableSizeInBytes;
    199  }();
    200 
    201  resources.MaxPrivateVariableSizeInBytes = [&]() -> size_t {
    202    const auto bytes = StaticPrefs::webgl_glsl_max_private_var_size_in_bytes();
    203    if (bytes >= 0) {
    204      return static_cast<size_t>(bytes);
    205    }
    206 
    207    if (kIsMacOS) {
    208      return 128 * 1024;  // 8k vec4s
    209    }
    210 
    211    return resources.MaxPrivateVariableSizeInBytes;
    212  }();
    213 
    214  // -
    215 
    216  const auto compileOptions =
    217      webgl::ChooseValidatorCompileOptions(resources, gl);
    218  auto ret = webgl::ShaderValidator::Create(shaderType, spec, outputLanguage,
    219                                            resources, compileOptions);
    220  if (!ret) return ret;
    221 
    222  ret->mIfNeeded_webgl_gl_VertexID_Offset |=
    223      mBug_DrawArraysInstancedUserAttribFetchAffectedByFirst;
    224 
    225  return ret;
    226 }
    227 
    228 ////////////////////////////////////////
    229 
    230 namespace webgl {
    231 
    232 /*static*/
    233 std::unique_ptr<ShaderValidator> ShaderValidator::Create(
    234    GLenum shaderType, ShShaderSpec spec, ShShaderOutput outputLanguage,
    235    const ShBuiltInResources& resources, ShCompileOptions compileOptions) {
    236  ShHandle handle =
    237      sh::ConstructCompiler(shaderType, spec, outputLanguage, &resources);
    238  MOZ_RELEASE_ASSERT(handle);
    239  if (!handle) return nullptr;
    240 
    241  return std::unique_ptr<ShaderValidator>(
    242      new ShaderValidator(handle, compileOptions, resources.MaxVaryingVectors));
    243 }
    244 
    245 ShaderValidator::~ShaderValidator() { sh::Destruct(mHandle); }
    246 
    247 inline bool StartsWith(const std::string_view& str,
    248                       const std::string_view& part) {
    249  return str.find(part) == 0;
    250 }
    251 
    252 inline std::vector<std::string_view> Split(std::string_view src,
    253                                           const std::string_view& delim,
    254                                           const size_t maxSplits = -1) {
    255  std::vector<std::string_view> ret;
    256  for (const auto i : IntegerRange(maxSplits)) {
    257    (void)i;
    258    const auto end = src.find(delim);
    259    if (end == size_t(-1)) {
    260      break;
    261    }
    262    ret.push_back(src.substr(0, end));
    263    src = src.substr(end + delim.size());
    264  }
    265  ret.push_back(src);
    266  return ret;
    267 }
    268 
    269 std::unique_ptr<const ShaderValidatorResults>
    270 ShaderValidator::ValidateAndTranslate(const char* const source) const {
    271  auto ret = std::make_unique<ShaderValidatorResults>();
    272 
    273  const std::array<const char*, 1> parts = {source};
    274  ret->mValid =
    275      sh::Compile(mHandle, parts.data(), parts.size(), mCompileOptions);
    276 
    277  ret->mInfoLog = sh::GetInfoLog(mHandle);
    278 
    279  if (ret->mValid) {
    280    ret->mObjectCode = sh::GetObjectCode(mHandle);
    281    ret->mShaderVersion = sh::GetShaderVersion(mHandle);
    282    ret->mVertexShaderNumViews = sh::GetVertexShaderNumViews(mHandle);
    283 
    284    ret->mAttributes = *sh::GetAttributes(mHandle);
    285    ret->mInterfaceBlocks = *sh::GetInterfaceBlocks(mHandle);
    286    ret->mOutputVariables = *sh::GetOutputVariables(mHandle);
    287    ret->mUniforms = *sh::GetUniforms(mHandle);
    288    ret->mVaryings = *sh::GetVaryings(mHandle);
    289 
    290    ret->mMaxVaryingVectors = mMaxVaryingVectors;
    291 
    292    const auto& nameMap = *sh::GetNameHashingMap(mHandle);
    293    for (const auto& pair : nameMap) {
    294      ret->mNameMap.insert(pair);
    295    }
    296 
    297    // -
    298    // Custom translation steps
    299    auto* const translatedSource = &ret->mObjectCode;
    300 
    301    // gl_VertexID -> webgl_gl_VertexID
    302    // gl_InstanceID -> webgl_gl_InstanceID
    303 
    304    std::string header;
    305    std::string_view body = *translatedSource;
    306    if (StartsWith(body, "#version")) {
    307      const auto parts = Split(body, "\n", 1);
    308      header = parts.at(0);
    309      header += "\n";
    310      body = parts.at(1);
    311    }
    312 
    313    for (const auto& attrib : ret->mAttributes) {
    314      if (mIfNeeded_webgl_gl_VertexID_Offset && attrib.name == "gl_VertexID" &&
    315          attrib.staticUse) {
    316        header += "uniform int webgl_gl_VertexID_Offset;\n";
    317        header +=
    318            "#define gl_VertexID (gl_VertexID + webgl_gl_VertexID_Offset)\n";
    319        ret->mNeeds_webgl_gl_VertexID_Offset = true;
    320      }
    321    }
    322 
    323    if (header.size()) {
    324      auto combined = header;
    325      combined += body;
    326      *translatedSource = combined;
    327    }
    328  }
    329 
    330  sh::ClearResults(mHandle);
    331  return ret;
    332 }
    333 
    334 bool ShaderValidatorResults::CanLinkTo(const ShaderValidatorResults& vert,
    335                                       nsCString* const out_log) const {
    336  MOZ_ASSERT(mValid);
    337  MOZ_ASSERT(vert.mValid);
    338 
    339  if (vert.mShaderVersion != mShaderVersion) {
    340    nsPrintfCString error(
    341        "Vertex shader version %d does not match"
    342        " fragment shader version %d.",
    343        vert.mShaderVersion, mShaderVersion);
    344    *out_log = error;
    345    return false;
    346  }
    347 
    348  for (const auto& itrFrag : mUniforms) {
    349    for (const auto& itrVert : vert.mUniforms) {
    350      if (itrVert.name != itrFrag.name) continue;
    351 
    352      if (!itrVert.isSameUniformAtLinkTime(itrFrag)) {
    353        nsPrintfCString error(
    354            "Uniform `%s` is not linkable between"
    355            " attached shaders.",
    356            itrFrag.name.c_str());
    357        *out_log = error;
    358        return false;
    359      }
    360 
    361      break;
    362    }
    363  }
    364 
    365  for (const auto& fragVar : mInterfaceBlocks) {
    366    for (const auto& vertVar : vert.mInterfaceBlocks) {
    367      if (vertVar.name != fragVar.name) continue;
    368 
    369      if (!vertVar.isSameInterfaceBlockAtLinkTime(fragVar)) {
    370        nsPrintfCString error(
    371            "Interface block `%s` is not linkable between"
    372            " attached shaders.",
    373            fragVar.name.c_str());
    374        *out_log = error;
    375        return false;
    376      }
    377 
    378      break;
    379    }
    380  }
    381 
    382  {
    383    std::vector<sh::ShaderVariable> staticUseVaryingList;
    384 
    385    for (const auto& fragVarying : mVaryings) {
    386      static const char prefix[] = "gl_";
    387      if (StartsWith(fragVarying.name, prefix)) {
    388        if (fragVarying.staticUse) {
    389          staticUseVaryingList.push_back(fragVarying);
    390        }
    391        continue;
    392      }
    393 
    394      bool definedInVertShader = false;
    395      bool staticVertUse = false;
    396 
    397      for (const auto& vertVarying : vert.mVaryings) {
    398        if (vertVarying.name != fragVarying.name) continue;
    399 
    400        if (!vertVarying.isSameVaryingAtLinkTime(fragVarying, mShaderVersion)) {
    401          nsPrintfCString error(
    402              "Varying `%s`is not linkable between"
    403              " attached shaders.",
    404              fragVarying.name.c_str());
    405          *out_log = error;
    406          return false;
    407        }
    408 
    409        definedInVertShader = true;
    410        staticVertUse = vertVarying.staticUse;
    411        break;
    412      }
    413 
    414      if (!definedInVertShader && fragVarying.staticUse) {
    415        nsPrintfCString error(
    416            "Varying `%s` has static-use in the frag"
    417            " shader, but is undeclared in the vert"
    418            " shader.",
    419            fragVarying.name.c_str());
    420        *out_log = error;
    421        return false;
    422      }
    423 
    424      if (staticVertUse && fragVarying.staticUse) {
    425        staticUseVaryingList.push_back(fragVarying);
    426      }
    427    }
    428 
    429    if (!sh::CheckVariablesWithinPackingLimits(mMaxVaryingVectors,
    430                                               staticUseVaryingList)) {
    431      *out_log =
    432          "Statically used varyings do not fit within packing limits. (see"
    433          " GLSL ES Specification 1.0.17, p111)";
    434      return false;
    435    }
    436  }
    437 
    438  if (mShaderVersion == 100) {
    439    // Enforce ESSL1 invariant linking rules.
    440    bool isInvariant_Position = false;
    441    bool isInvariant_PointSize = false;
    442    bool isInvariant_FragCoord = false;
    443    bool isInvariant_PointCoord = false;
    444 
    445    for (const auto& varying : vert.mVaryings) {
    446      if (varying.name == "gl_Position") {
    447        isInvariant_Position = varying.isInvariant;
    448      } else if (varying.name == "gl_PointSize") {
    449        isInvariant_PointSize = varying.isInvariant;
    450      }
    451    }
    452 
    453    for (const auto& varying : mVaryings) {
    454      if (varying.name == "gl_FragCoord") {
    455        isInvariant_FragCoord = varying.isInvariant;
    456      } else if (varying.name == "gl_PointCoord") {
    457        isInvariant_PointCoord = varying.isInvariant;
    458      }
    459    }
    460 
    461    ////
    462 
    463    const auto fnCanBuiltInsLink = [](bool vertIsInvariant,
    464                                      bool fragIsInvariant) {
    465      if (vertIsInvariant) return true;
    466 
    467      return !fragIsInvariant;
    468    };
    469 
    470    if (!fnCanBuiltInsLink(isInvariant_Position, isInvariant_FragCoord)) {
    471      *out_log =
    472          "gl_Position must be invariant if gl_FragCoord is. (see GLSL ES"
    473          " Specification 1.0.17, p39)";
    474      return false;
    475    }
    476 
    477    if (!fnCanBuiltInsLink(isInvariant_PointSize, isInvariant_PointCoord)) {
    478      *out_log =
    479          "gl_PointSize must be invariant if gl_PointCoord is. (see GLSL ES"
    480          " Specification 1.0.17, p39)";
    481      return false;
    482    }
    483  }
    484 
    485  return true;
    486 }
    487 
    488 size_t ShaderValidatorResults::SizeOfIncludingThis(
    489    const MallocSizeOf fnSizeOf) const {
    490  auto ret = fnSizeOf(this);
    491  ret += mInfoLog.size();
    492  ret += mObjectCode.size();
    493 
    494  for (const auto& cur : mAttributes) {
    495    ret += fnSizeOf(&cur);
    496  }
    497  for (const auto& cur : mInterfaceBlocks) {
    498    ret += fnSizeOf(&cur);
    499  }
    500  for (const auto& cur : mOutputVariables) {
    501    ret += fnSizeOf(&cur);
    502  }
    503  for (const auto& cur : mUniforms) {
    504    ret += fnSizeOf(&cur);
    505  }
    506  for (const auto& cur : mVaryings) {
    507    ret += fnSizeOf(&cur);
    508  }
    509 
    510  return ret;
    511 }
    512 
    513 }  // namespace webgl
    514 }  // namespace mozilla