tor-browser

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

SanitizeRenderer.cpp (11696B)


      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 <functional>
      7 #include <regex>
      8 #include <string>
      9 
     10 #include "mozilla/gfx/Logging.h"
     11 
     12 namespace mozilla {
     13 namespace webgl {
     14 
     15 static bool Contains(const std::string& str, const std::string& part) {
     16  return str.find(part) != size_t(-1);
     17 }
     18 
     19 /**
     20 * Narrow renderer string space down to representative replacements.
     21 * E.g. "GeForce RTX 3090" => "GeForce GTX 980"
     22 *
     23 * For example strings:
     24 * https://hackmd.io/Ductv3pQTMej74gbveD4yw
     25 */
     26 static std::optional<std::string> ChooseDeviceReplacement(
     27    const std::string& str) {
     28  if (str.find("llvmpipe") == 0) return "llvmpipe";
     29  if (str.find("Apple") == 0) return "Apple M1";
     30 
     31  std::smatch m;
     32 
     33  // -
     34  // AMD
     35 
     36  {
     37    static const std::string RADEON_HD_3000 = "Radeon HD 3200 Graphics";
     38    static const std::string RADEON_HD_5850 = "Radeon HD 5850";
     39    static const std::string RADEON_R9_290 = "Radeon R9 200 Series";
     40    const auto& RADEON_D3D_FL10_1 = RADEON_HD_3000;
     41 
     42    if (Contains(str, "REMBRANDT")) {  // Mobile 6xxx iGPUs
     43      return RADEON_R9_290;
     44    }
     45    if (Contains(str, "RENOIR")) {  // Desktop 4xxxG iGPUs
     46      return RADEON_R9_290;
     47    }
     48    if (Contains(str, "Vega")) {
     49      return RADEON_R9_290;
     50    }
     51    if (Contains(str, "VII")) {
     52      return RADEON_R9_290;
     53    }
     54    if (Contains(str, "Fury")) {
     55      return RADEON_R9_290;
     56    }
     57 
     58    static const std::regex kRadeon(
     59        "Radeon.*?((R[579X]|HD) )?([0-9][0-9][0-9]+)");
     60    if (std::regex_search(str, m, kRadeon)) {
     61      const auto& rxOrHd = m.str(2);
     62      const auto modelNum = stoul(m.str(3));
     63      if (rxOrHd == "HD") {
     64        if (modelNum >= 5000) {
     65          return RADEON_HD_5850;
     66        }
     67        if (modelNum >= 3000) {
     68          return RADEON_HD_3000;  // FL10_1
     69        }
     70        // HD 2000 is FL10_0, but webgl2 needs 10_1, so claim "old".
     71        return RADEON_D3D_FL10_1;
     72      }
     73      // R5/7/9/X
     74      return RADEON_R9_290;
     75    }
     76 
     77    static const std::regex kFirePro("FirePro.*?([VDW])[0-9][0-9][0-9]+");
     78    if (std::regex_search(str, m, kFirePro)) {
     79      const auto& vdw = m.str(1);
     80      if (vdw == "V") {
     81        return RADEON_D3D_FL10_1;  // FL10_1
     82      }
     83      return RADEON_R9_290;
     84    }
     85 
     86    if (Contains(str, "ARUBA")) {
     87      return RADEON_HD_5850;
     88    }
     89 
     90    if (Contains(str, "AMD ") || Contains(str, "FirePro") ||
     91        Contains(str, "Radeon")) {
     92      return RADEON_D3D_FL10_1;
     93    }
     94  }
     95 
     96  // -
     97 
     98  static const std::string GEFORCE_8800 = "GeForce 8800 GTX";
     99  static const std::string GEFORCE_480 = "GeForce GTX 480";
    100  static const std::string GEFORCE_980 = "GeForce GTX 980";
    101 
    102  if (Contains(str, "NVIDIA") || Contains(str, "GeForce") ||
    103      Contains(str, "Quadro")) {
    104    auto ret = std::invoke([&]() {
    105      static const std::regex kGeForce("GeForce.*?([0-9][0-9][0-9]+)");
    106      if (std::regex_search(str, m, kGeForce)) {
    107        const auto modelNum = stoul(m.str(1));
    108        if (modelNum >= 8000) {
    109          // Tesla+: D3D10.0, SM4.0
    110          return GEFORCE_8800;
    111        }
    112        if (modelNum >= 900) {
    113          // Maxwell Gen2+: D3D12 FL12_1
    114          return GEFORCE_980;
    115        }
    116        if (modelNum >= 400) {
    117          // Fermi+: D3D12 FL11_0
    118          return GEFORCE_480;
    119        }
    120        // Tesla+: D3D10.0, SM4.0
    121        return GEFORCE_8800;
    122      }
    123 
    124      static const std::regex kQuadro("Quadro.*?([KMPVT]?)[0-9][0-9][0-9]+");
    125      if (std::regex_search(str, m, kQuadro)) {
    126        if (Contains(str, "RTX")) return GEFORCE_980;
    127        const auto archLetter = m.str(1);
    128        if (!archLetter.empty()) {
    129          switch (archLetter[0]) {
    130            case 'M':  // Maxwell
    131            case 'P':  // Pascal
    132            case 'V':  // Volta
    133            case 'T':  // Turing, mobile-only
    134              return GEFORCE_980;
    135            case 'K':  // Kepler
    136            default:
    137              return GEFORCE_480;
    138          }
    139        }
    140        return GEFORCE_8800;
    141      }
    142 
    143      /* Similarities for Titans:
    144       * 780
    145       * * GeForce GTX TITAN
    146       * * -
    147       * * Black
    148       * * Z
    149       * 980
    150       * * GeForce GTX TITAN X
    151       * 1080
    152       * * Nvidia TITAN X
    153       * * Nvidia TITAN Xp
    154       * * Nvidia TITAN V
    155       * 2080
    156       * * Nvidia TITAN RTX
    157       */
    158      static const std::regex kTitan("TITAN( [BZXVR])?");
    159      if (std::regex_search(str, m, kTitan)) {
    160        char letter = ' ';
    161        const auto sub = m.str(1);
    162        if (sub.length()) {
    163          letter = sub[1];
    164        }
    165        switch (letter) {
    166          case ' ':
    167          case 'B':
    168          case 'Z':
    169            return GEFORCE_480;
    170          default:
    171            return GEFORCE_980;
    172        }
    173      }
    174      // CI has str:"Tesla M60"
    175      if (Contains(str, "Tesla")) return GEFORCE_8800;
    176 
    177      return GEFORCE_8800;  // Unknown, but NV.
    178    });
    179    // On ANGLE: NVIDIA GeForce RTX 3070...
    180    // On WGL: GeForce RTX 3070...
    181    if (str.find("NVIDIA") == 0) {
    182      ret = "NVIDIA " + ret;
    183    }
    184    return ret;
    185  }
    186 
    187  static const std::regex kNouveau("NV(1?[0-9A-F][0-9A-F])");
    188  if (std::regex_match(str, m, kNouveau)) {
    189    const auto modelNum = stoul(m.str(1), nullptr, 16);
    190    // https://nouveau.freedesktop.org/CodeNames.html#NV110
    191    if (modelNum >= 0x120) return GEFORCE_980;
    192    if (modelNum >= 0xC0) return GEFORCE_480;
    193    return GEFORCE_8800;
    194  }
    195 
    196  // -
    197 
    198  if (Contains(str, "Intel")) {
    199    static const std::string HD_GRAPHICS = "Intel(R) HD Graphics";
    200    static const std::string HD_GRAPHICS_400 = "Intel(R) HD Graphics 400";
    201    static const std::string INTEL_945GM = "Intel 945GM";
    202    // Pick A750 to split the performance difference, but err optimistically on
    203    // the high end.
    204    static const std::string DGPU_ARC = "Intel(R) Arc(TM) A750 Graphics";
    205 
    206    if (Contains(str, "Intel(R) Arc(TM)")) {
    207      return DGPU_ARC;
    208    }
    209 
    210    static const std::regex kIntelHD("Intel.*Graphics( P?([0-9][0-9][0-9]+))?");
    211    if (std::regex_search(str, m, kIntelHD)) {
    212      if (m.str(1).empty()) {
    213        return HD_GRAPHICS;
    214      }
    215      const auto modelNum = stoul(m.str(2));
    216      if (modelNum >= 5000) {
    217        return HD_GRAPHICS_400;
    218      }
    219      if (modelNum >= 1000) {
    220        return HD_GRAPHICS;
    221      }
    222      return HD_GRAPHICS_400;
    223    }
    224 
    225    return INTEL_945GM;
    226  }
    227 
    228  // -
    229 
    230  static const std::regex kAdreno("Adreno.*?([0-9][0-9][0-9]+)");
    231  if (std::regex_search(str, m, kAdreno)) {
    232    const auto modelNum = stoul(m.str(1));
    233    if (modelNum >= 600) {
    234      return "Adreno (TM) 650";
    235    }
    236    if (modelNum >= 500) {
    237      return "Adreno (TM) 540";
    238    }
    239    if (modelNum >= 400) {
    240      return "Adreno (TM) 430";
    241    }
    242    if (modelNum >= 300) {
    243      return "Adreno (TM) 330";
    244    }
    245    return "Adreno (TM) 225";
    246  }
    247 
    248  static const std::regex kMali("Mali.*?([0-9][0-9]+)");
    249  if (std::regex_search(str, m, kMali)) {
    250    const auto modelNum = stoul(m.str(1));
    251    if (modelNum >= 800) {
    252      return "Mali-T880";
    253    }
    254    if (modelNum >= 700) {
    255      return "Mali-T760";
    256    }
    257    if (modelNum >= 600) {
    258      return "Mali-T628";
    259    }
    260    if (modelNum >= 400) {
    261      return "Mali-400 MP";
    262    }
    263    return "Mali-G51";
    264  }
    265 
    266  if (Contains(str, "PowerVR")) {
    267    if (Contains(str, "Rogue")) {
    268      return "PowerVR Rogue G6200";
    269    }
    270    return "PowerVR SGX 540";
    271  }
    272 
    273  if (Contains(str, "Samsung Xclipse")) {
    274    return "Samsung Xclipse 920";
    275  }
    276 
    277  if (Contains(str, "Vivante")) return "Vivante GC1000";
    278  if (Contains(str, "VideoCore")) return "VideoCore IV HW";
    279  if (Contains(str, "Tegra")) return "NVIDIA Tegra";
    280 
    281  // -
    282 
    283  static const std::string D3D_WARP = "Microsoft Basic Render Driver";
    284  if (Contains(str, D3D_WARP)) return str;
    285 
    286  return {};
    287 }
    288 
    289 // -
    290 
    291 std::string SanitizeRenderer(const std::string& raw_renderer) {
    292  std::smatch m;
    293 
    294  const std::string GENERIC_RENDERER = "Generic Renderer";
    295 
    296  const auto replacementDevice = [&]() -> std::optional<std::string> {
    297    // e.g. "ANGLE (AMD, AMD Radeon(TM) Graphics Direct3D11 vs_5_0 ps_5_0,
    298    // D3D11-27.20.1020.2002)"
    299    static const std::regex kReAngleDirect3D(
    300        "ANGLE [(]([^,]*), ([^,]*)( Direct3D[^,]*), .*[)]");
    301    // e.g. "ANGLE (Samsung Xclipse 940) on Vulkan 1.3.264"
    302    static const std::regex kReAngleVulkan(
    303        "ANGLE [(]+(.*)[)]( on Vulkan) [0-9\\.]*[)]*");
    304 
    305    if (std::regex_match(raw_renderer, m, kReAngleDirect3D)) {
    306      const auto& vendor = m.str(1);
    307      const auto& renderer = m.str(2);
    308      const auto& d3d_suffix = m.str(3);
    309 
    310      auto renderer2 = ChooseDeviceReplacement(renderer);
    311      if (!renderer2) {
    312        gfxCriticalNote << "Couldn't sanitize Direct3D ANGLE renderer \""
    313                        << renderer << "\" from GL_RENDERER \"" << raw_renderer;
    314        renderer2 = GENERIC_RENDERER;
    315      }
    316      return std::string("ANGLE (") + vendor + ", " + *renderer2 + d3d_suffix +
    317             ")";
    318    } else if (std::regex_match(raw_renderer, m, kReAngleVulkan)) {
    319      const auto& renderer = m.str(1);
    320      const auto& vulkan_suffix = m.str(2);
    321 
    322      auto renderer2 = ChooseDeviceReplacement(renderer);
    323      if (!renderer2) {
    324        gfxCriticalNote << "Couldn't sanitize Vulkan ANGLE renderer \""
    325                        << renderer << "\" from GL_RENDERER \"" << raw_renderer;
    326        renderer2 = GENERIC_RENDERER;
    327      }
    328      return std::string("ANGLE (") + *renderer2 + ")" + vulkan_suffix;
    329    } else if (Contains(raw_renderer, "ANGLE")) {
    330      gfxCriticalError() << "Failed to parse ANGLE renderer: " << raw_renderer;
    331      return {};
    332    }
    333 
    334    static const std::regex kReOpenglEngine("(.*) OpenGL Engine");
    335    static const std::regex kRePcieSse2("(.*)(/PCIe?/SSE2)");
    336    static const std::regex kReStandard("(.*)( [(].*[)])");
    337    if (std::regex_match(raw_renderer, m, kReOpenglEngine)) {
    338      const auto& dev = m.str(1);
    339      return ChooseDeviceReplacement(dev);
    340    }
    341    if (std::regex_match(raw_renderer, m, kRePcieSse2)) {
    342      const auto& dev = m.str(1);
    343      return ChooseDeviceReplacement(dev);
    344    }
    345    if (std::regex_match(raw_renderer, m, kReStandard)) {
    346      const auto& dev = m.str(1);
    347      return ChooseDeviceReplacement(dev);
    348    }
    349 
    350    const auto& dev = raw_renderer;
    351    return ChooseDeviceReplacement(dev);
    352  }();
    353 
    354  if (!replacementDevice) {
    355    gfxCriticalNote << "Couldn't sanitize GL_RENDERER \"" << raw_renderer
    356                    << "\"";
    357    return GENERIC_RENDERER;
    358  }
    359 
    360  return *replacementDevice + ", or similar";
    361 }
    362 
    363 // -
    364 
    365 /**
    366 * Sanitize vendor string to standardized buckets.
    367 * E.g. "NVIDIA Corporation" => "NVIDIA Corporation"
    368 */
    369 std::string SanitizeVendor(const std::string& raw_vendor) {
    370  if (Contains(raw_vendor, "NVIDIA")) {
    371    return "NVIDIA Corporation";
    372  }
    373  if (Contains(raw_vendor, "Intel")) {
    374    return "Intel";
    375  }
    376  if (Contains(raw_vendor, "AMD") || Contains(raw_vendor, "ATI") ||
    377      Contains(raw_vendor, "Advanced Micro Devices")) {
    378    return "AMD";
    379  }
    380  if (Contains(raw_vendor, "Qualcomm")) {
    381    return "Qualcomm";
    382  }
    383  if (Contains(raw_vendor, "ARM")) {
    384    return "ARM";
    385  }
    386  if (Contains(raw_vendor, "Apple")) {
    387    return "Apple";
    388  }
    389  if (Contains(raw_vendor, "Samsung")) {
    390    return "Samsung";
    391  }
    392  if (Contains(raw_vendor, "Mesa") || Contains(raw_vendor, "X.Org")) {
    393    return "Mesa";
    394  }
    395  if (Contains(raw_vendor, "Microsoft")) {
    396    return "Microsoft";
    397  }
    398  if (Contains(raw_vendor, "VMware")) {
    399    return "VMware";
    400  }
    401  if (Contains(raw_vendor, "Google")) {
    402    return "Google";
    403  }
    404 
    405  return "Other";
    406 }
    407 
    408 };  // namespace webgl
    409 };  // namespace mozilla