Bug 1936278 - Prevent search mode chiclet from being dismissed when clicking in page...
[gecko.git] / dom / canvas / SanitizeRenderer.cpp
blob19fd3b8ea037e86eefff5049d0a370fe4a31f3d7
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 "mozilla/gfx/Logging.h"
7 #include "mozilla/IntegerRange.h"
9 #include <functional>
10 #include <regex>
11 #include <string>
13 namespace mozilla {
14 namespace webgl {
16 static bool Contains(const std::string& str, const std::string& part) {
17 return str.find(part) != size_t(-1);
20 /**
21 * Narrow renderer string space down to representative replacements.
22 * E.g. "GeForce RTX 3090" => "GeForce GTX 980"
24 * For example strings:
25 * https://hackmd.io/Ductv3pQTMej74gbveD4yw
27 static std::optional<std::string> ChooseDeviceReplacement(
28 const std::string& str) {
29 if (str.find("llvmpipe") == 0) return "llvmpipe";
30 if (str.find("Apple") == 0) return "Apple M1";
32 std::smatch m;
34 // -
35 // AMD
38 static const std::string RADEON_HD_3000 = "Radeon HD 3200 Graphics";
39 static const std::string RADEON_HD_5850 = "Radeon HD 5850";
40 static const std::string RADEON_R9_290 = "Radeon R9 200 Series";
41 const auto& RADEON_D3D_FL10_1 = RADEON_HD_3000;
43 if (Contains(str, "REMBRANDT")) { // Mobile 6xxx iGPUs
44 return RADEON_R9_290;
46 if (Contains(str, "RENOIR")) { // Desktop 4xxxG iGPUs
47 return RADEON_R9_290;
49 if (Contains(str, "Vega")) {
50 return RADEON_R9_290;
52 if (Contains(str, "VII")) {
53 return RADEON_R9_290;
55 if (Contains(str, "Fury")) {
56 return RADEON_R9_290;
59 static const std::regex kRadeon(
60 "Radeon.*?((R[579X]|HD) )?([0-9][0-9][0-9]+)");
61 if (std::regex_search(str, m, kRadeon)) {
62 const auto& rxOrHd = m.str(2);
63 const auto modelNum = stoul(m.str(3));
64 if (rxOrHd == "HD") {
65 if (modelNum >= 5000) {
66 return RADEON_HD_5850;
68 if (modelNum >= 3000) {
69 return RADEON_HD_3000; // FL10_1
71 // HD 2000 is FL10_0, but webgl2 needs 10_1, so claim "old".
72 return RADEON_D3D_FL10_1;
74 // R5/7/9/X
75 return RADEON_R9_290;
78 static const std::regex kFirePro("FirePro.*?([VDW])[0-9][0-9][0-9]+");
79 if (std::regex_search(str, m, kFirePro)) {
80 const auto& vdw = m.str(1);
81 if (vdw == "V") {
82 return RADEON_D3D_FL10_1; // FL10_1
84 return RADEON_R9_290;
87 if (Contains(str, "ARUBA")) {
88 return RADEON_HD_5850;
91 if (Contains(str, "AMD ") || Contains(str, "FirePro") ||
92 Contains(str, "Radeon")) {
93 return RADEON_D3D_FL10_1;
97 // -
99 static const std::string GEFORCE_8800 = "GeForce 8800 GTX";
100 static const std::string GEFORCE_480 = "GeForce GTX 480";
101 static const std::string GEFORCE_980 = "GeForce GTX 980";
103 if (Contains(str, "NVIDIA") || Contains(str, "GeForce") ||
104 Contains(str, "Quadro")) {
105 auto ret = std::invoke([&]() {
106 static const std::regex kGeForce("GeForce.*?([0-9][0-9][0-9]+)");
107 if (std::regex_search(str, m, kGeForce)) {
108 const auto modelNum = stoul(m.str(1));
109 if (modelNum >= 8000) {
110 // Tesla+: D3D10.0, SM4.0
111 return GEFORCE_8800;
113 if (modelNum >= 900) {
114 // Maxwell Gen2+: D3D12 FL12_1
115 return GEFORCE_980;
117 if (modelNum >= 400) {
118 // Fermi+: D3D12 FL11_0
119 return GEFORCE_480;
121 // Tesla+: D3D10.0, SM4.0
122 return GEFORCE_8800;
125 static const std::regex kQuadro("Quadro.*?([KMPVT]?)[0-9][0-9][0-9]+");
126 if (std::regex_search(str, m, kQuadro)) {
127 if (Contains(str, "RTX")) return GEFORCE_980;
128 const auto archLetter = m.str(1);
129 if (!archLetter.empty()) {
130 switch (archLetter[0]) {
131 case 'M': // Maxwell
132 case 'P': // Pascal
133 case 'V': // Volta
134 case 'T': // Turing, mobile-only
135 return GEFORCE_980;
136 case 'K': // Kepler
137 default:
138 return GEFORCE_480;
141 return GEFORCE_8800;
144 /* Similarities for Titans:
145 * 780
146 * * GeForce GTX TITAN
147 * * -
148 * * Black
149 * * Z
150 * 980
151 * * GeForce GTX TITAN X
152 * 1080
153 * * Nvidia TITAN X
154 * * Nvidia TITAN Xp
155 * * Nvidia TITAN V
156 * 2080
157 * * Nvidia TITAN RTX
159 static const std::regex kTitan("TITAN( [BZXVR])?");
160 if (std::regex_search(str, m, kTitan)) {
161 char letter = ' ';
162 const auto sub = m.str(1);
163 if (sub.length()) {
164 letter = sub[1];
166 switch (letter) {
167 case ' ':
168 case 'B':
169 case 'Z':
170 return GEFORCE_480;
171 default:
172 return GEFORCE_980;
175 // CI has str:"Tesla M60"
176 if (Contains(str, "Tesla")) return GEFORCE_8800;
178 return GEFORCE_8800; // Unknown, but NV.
180 // On ANGLE: NVIDIA GeForce RTX 3070...
181 // On WGL: GeForce RTX 3070...
182 if (str.find("NVIDIA") == 0) {
183 ret = "NVIDIA " + ret;
185 return ret;
188 static const std::regex kNouveau("NV(1?[0-9A-F][0-9A-F])");
189 if (std::regex_match(str, m, kNouveau)) {
190 const auto modelNum = stoul(m.str(1), nullptr, 16);
191 // https://nouveau.freedesktop.org/CodeNames.html#NV110
192 if (modelNum >= 0x120) return GEFORCE_980;
193 if (modelNum >= 0xC0) return GEFORCE_480;
194 return GEFORCE_8800;
197 // -
199 if (Contains(str, "Intel")) {
200 static const std::string HD_GRAPHICS = "Intel(R) HD Graphics";
201 static const std::string HD_GRAPHICS_400 = "Intel(R) HD Graphics 400";
202 static const std::string INTEL_945GM = "Intel 945GM";
203 // Pick A750 to split the performance difference, but err optimistically on
204 // the high end.
205 static const std::string DGPU_ARC = "Intel(R) Arc(TM) A750 Graphics";
207 if (Contains(str, "Intel(R) Arc(TM)")) {
208 return DGPU_ARC;
211 static const std::regex kIntelHD("Intel.*Graphics( P?([0-9][0-9][0-9]+))?");
212 if (std::regex_search(str, m, kIntelHD)) {
213 if (m.str(1).empty()) {
214 return HD_GRAPHICS;
216 const auto modelNum = stoul(m.str(2));
217 if (modelNum >= 5000) {
218 return HD_GRAPHICS_400;
220 if (modelNum >= 1000) {
221 return HD_GRAPHICS;
223 return HD_GRAPHICS_400;
226 return INTEL_945GM;
229 // -
231 static const std::regex kAdreno("Adreno.*?([0-9][0-9][0-9]+)");
232 if (std::regex_search(str, m, kAdreno)) {
233 const auto modelNum = stoul(m.str(1));
234 if (modelNum >= 600) {
235 return "Adreno (TM) 650";
237 if (modelNum >= 500) {
238 return "Adreno (TM) 540";
240 if (modelNum >= 400) {
241 return "Adreno (TM) 430";
243 if (modelNum >= 300) {
244 return "Adreno (TM) 330";
246 return "Adreno (TM) 225";
249 static const std::regex kMali("Mali.*?([0-9][0-9]+)");
250 if (std::regex_search(str, m, kMali)) {
251 const auto modelNum = stoul(m.str(1));
252 if (modelNum >= 800) {
253 return "Mali-T880";
255 if (modelNum >= 700) {
256 return "Mali-T760";
258 if (modelNum >= 600) {
259 return "Mali-T628";
261 if (modelNum >= 400) {
262 return "Mali-400 MP";
264 return "Mali-G51";
267 if (Contains(str, "PowerVR")) {
268 if (Contains(str, "Rogue")) {
269 return "PowerVR Rogue G6200";
271 return "PowerVR SGX 540";
274 if (Contains(str, "Vivante")) return "Vivante GC1000";
275 if (Contains(str, "VideoCore")) return "VideoCore IV HW";
276 if (Contains(str, "Tegra")) return "NVIDIA Tegra";
278 // -
280 static const std::string D3D_WARP = "Microsoft Basic Render Driver";
281 if (Contains(str, D3D_WARP)) return str;
283 return {};
286 // -
288 std::string SanitizeRenderer(const std::string& raw_renderer) {
289 std::smatch m;
291 const std::string GENERIC_RENDERER = "Generic Renderer";
293 const auto replacementDevice = [&]() -> std::optional<std::string> {
294 // e.g. "ANGLE (AMD, AMD Radeon(TM) Graphics Direct3D11 vs_5_0 ps_5_0,
295 // D3D11-27.20.1020.2002)"
296 static const std::regex kReAngle(
297 "ANGLE [(]([^,]*), ([^,]*)( Direct3D[^,]*), .*[)]");
298 if (std::regex_match(raw_renderer, m, kReAngle)) {
299 const auto& vendor = m.str(1);
300 const auto& renderer = m.str(2);
301 const auto& d3d_suffix = m.str(3);
303 auto renderer2 = ChooseDeviceReplacement(renderer);
304 if (!renderer2) {
305 gfxCriticalNote << "Couldn't sanitize ANGLE renderer \"" << renderer
306 << "\" from GL_RENDERER \"" << raw_renderer;
307 renderer2 = GENERIC_RENDERER;
309 return std::string("ANGLE (") + vendor + ", " + *renderer2 + d3d_suffix +
310 ")";
311 } else if (Contains(raw_renderer, "ANGLE")) {
312 gfxCriticalError() << "Failed to parse ANGLE renderer: " << raw_renderer;
313 return {};
316 static const std::regex kReOpenglEngine("(.*) OpenGL Engine");
317 static const std::regex kRePcieSse2("(.*)(/PCIe?/SSE2)");
318 static const std::regex kReStandard("(.*)( [(].*[)])");
319 if (std::regex_match(raw_renderer, m, kReOpenglEngine)) {
320 const auto& dev = m.str(1);
321 return ChooseDeviceReplacement(dev);
323 if (std::regex_match(raw_renderer, m, kRePcieSse2)) {
324 const auto& dev = m.str(1);
325 return ChooseDeviceReplacement(dev);
327 if (std::regex_match(raw_renderer, m, kReStandard)) {
328 const auto& dev = m.str(1);
329 return ChooseDeviceReplacement(dev);
332 const auto& dev = raw_renderer;
333 return ChooseDeviceReplacement(dev);
334 }();
336 if (!replacementDevice) {
337 gfxCriticalNote << "Couldn't sanitize GL_RENDERER \"" << raw_renderer
338 << "\"";
339 return GENERIC_RENDERER;
342 return *replacementDevice + ", or similar";
345 }; // namespace webgl
346 }; // namespace mozilla