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"
16 static bool Contains(const std::string
& str
, const std::string
& part
) {
17 return str
.find(part
) != size_t(-1);
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";
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
46 if (Contains(str
, "RENOIR")) { // Desktop 4xxxG iGPUs
49 if (Contains(str
, "Vega")) {
52 if (Contains(str
, "VII")) {
55 if (Contains(str
, "Fury")) {
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));
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
;
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);
82 return RADEON_D3D_FL10_1
; // FL10_1
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
;
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
113 if (modelNum
>= 900) {
114 // Maxwell Gen2+: D3D12 FL12_1
117 if (modelNum
>= 400) {
118 // Fermi+: D3D12 FL11_0
121 // Tesla+: D3D10.0, SM4.0
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]) {
134 case 'T': // Turing, mobile-only
144 /* Similarities for Titans:
146 * * GeForce GTX TITAN
151 * * GeForce GTX TITAN X
159 static const std::regex
kTitan("TITAN( [BZXVR])?");
160 if (std::regex_search(str
, m
, kTitan
)) {
162 const auto sub
= m
.str(1);
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
;
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
;
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
205 static const std::string DGPU_ARC
= "Intel(R) Arc(TM) A750 Graphics";
207 if (Contains(str
, "Intel(R) Arc(TM)")) {
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()) {
216 const auto modelNum
= stoul(m
.str(2));
217 if (modelNum
>= 5000) {
218 return HD_GRAPHICS_400
;
220 if (modelNum
>= 1000) {
223 return HD_GRAPHICS_400
;
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) {
255 if (modelNum
>= 700) {
258 if (modelNum
>= 600) {
261 if (modelNum
>= 400) {
262 return "Mali-400 MP";
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";
280 static const std::string D3D_WARP
= "Microsoft Basic Render Driver";
281 if (Contains(str
, D3D_WARP
)) return str
;
288 std::string
SanitizeRenderer(const std::string
& raw_renderer
) {
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
);
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
+
311 } else if (Contains(raw_renderer
, "ANGLE")) {
312 gfxCriticalError() << "Failed to parse ANGLE renderer: " << raw_renderer
;
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
);
336 if (!replacementDevice
) {
337 gfxCriticalNote
<< "Couldn't sanitize GL_RENDERER \"" << raw_renderer
339 return GENERIC_RENDERER
;
342 return *replacementDevice
+ ", or similar";
345 }; // namespace webgl
346 }; // namespace mozilla