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::string
ChooseDeviceReplacement(const std::string
& str
) {
28 if (str
.find("llvmpipe") == 0) return "llvmpipe";
29 if (str
.find("Apple") == 0) return "Apple M1";
35 if (Contains(str
, "AMD ") || Contains(str
, "FirePro") ||
36 Contains(str
, "Radeon")) {
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";
41 const auto RADEON_D3D_FL10_1
= RADEON_HD_3000
;
42 const auto RADEON_GCN_GEN2
= RADEON_R9_290
; // GCN Gen2
44 if (Contains(str
, "Vega")) {
47 if (Contains(str
, "VII")) {
50 if (Contains(str
, "Fury")) {
53 static const std::regex
kRadeon(
54 "Radeon.*?((R[579X]|HD) )?([0-9][0-9][0-9]+)");
55 if (std::regex_search(str
, m
, kRadeon
)) {
56 const auto& rxOrHd
= m
.str(2);
57 const auto modelNum
= stoul(m
.str(3));
59 if (modelNum
>= 5000) {
60 return RADEON_HD_5850
;
62 if (modelNum
>= 3000) {
63 return RADEON_HD_3000
; // FL10_1
65 // HD 2000 is FL10_0, but webgl2 needs 10_1, so claim "old".
66 return RADEON_D3D_FL10_1
;
71 static const std::regex
kFirePro("FirePro.*?([VDW])[0-9][0-9][0-9]+");
72 if (std::regex_search(str
, m
, kFirePro
)) {
73 const auto& vdw
= m
.str(1);
75 return RADEON_HD_3000
; // FL10_1
80 if (Contains(str
, "RENOIR")) {
83 if (Contains(str
, "ARUBA")) {
84 return RADEON_HD_5850
;
87 return RADEON_D3D_FL10_1
;
92 static const std::string GEFORCE_8800
= "GeForce 8800 GTX";
93 static const std::string GEFORCE_480
= "GeForce GTX 480";
94 static const std::string GEFORCE_980
= "GeForce GTX 980";
96 if (Contains(str
, "NVIDIA") || Contains(str
, "GeForce") ||
97 Contains(str
, "Quadro")) {
98 auto ret
= std::invoke([&]() {
99 static const std::regex
kGeForce("GeForce.*?([0-9][0-9][0-9]+)");
100 if (std::regex_search(str
, m
, kGeForce
)) {
101 const auto modelNum
= stoul(m
.str(1));
102 if (modelNum
>= 8000) {
103 // Tesla+: D3D10.0, SM4.0
106 if (modelNum
>= 900) {
107 // Maxwell Gen2+: D3D12 FL12_1
110 if (modelNum
>= 400) {
111 // Fermi+: D3D12 FL11_0
114 // Tesla+: D3D10.0, SM4.0
118 static const std::regex
kQuadro("Quadro.*?([KMPVT]?)[0-9][0-9][0-9]+");
119 if (std::regex_search(str
, m
, kQuadro
)) {
120 if (Contains(str
, "RTX")) return GEFORCE_980
;
121 const auto archLetter
= m
.str(1);
122 if (!archLetter
.empty()) {
123 switch (archLetter
[0]) {
127 case 'T': // Turing, mobile-only
137 /* Similarities for Titans:
139 * * GeForce GTX TITAN
144 * * GeForce GTX TITAN X
152 static const std::regex
kTitan("TITAN( [BZXVR])?");
153 if (std::regex_search(str
, m
, kTitan
)) {
155 const auto sub
= m
.str(1);
168 // CI has str:"Tesla M60"
169 if (Contains(str
, "Tesla")) return GEFORCE_8800
;
171 return GEFORCE_8800
; // Unknown, but NV.
173 // On ANGLE: NVIDIA GeForce RTX 3070...
174 // On WGL: GeForce RTX 3070...
175 if (str
.find("NVIDIA") == 0) {
176 ret
= "NVIDIA " + ret
;
181 static const std::regex
kNouveau("NV(1?[0-9A-F][0-9A-F])");
182 if (std::regex_match(str
, m
, kNouveau
)) {
183 const auto modelNum
= stoul(m
.str(1), nullptr, 16);
184 // https://nouveau.freedesktop.org/CodeNames.html#NV110
185 if (modelNum
>= 0x120) return GEFORCE_980
;
186 if (modelNum
>= 0xC0) return GEFORCE_480
;
192 if (Contains(str
, "Intel")) {
193 static const std::string HD_GRAPHICS
= "Intel(R) HD Graphics";
194 static const std::string HD_GRAPHICS_400
= "Intel(R) HD Graphics 400";
195 static const std::string INTEL_945GM
= "Intel 945GM";
197 static const std::regex
kIntelHD("Intel.*Graphics( P?([0-9][0-9][0-9]+))?");
198 if (std::regex_search(str
, m
, kIntelHD
)) {
199 if (m
.str(1).empty()) {
202 const auto modelNum
= stoul(m
.str(2));
203 if (modelNum
>= 5000) {
204 return HD_GRAPHICS_400
;
206 if (modelNum
>= 1000) {
209 return HD_GRAPHICS_400
;
217 static const std::regex
kAdreno("Adreno.*?([0-9][0-9][0-9]+)");
218 if (std::regex_search(str
, m
, kAdreno
)) {
219 const auto modelNum
= stoul(m
.str(1));
220 if (modelNum
>= 600) {
221 return "Adreno (TM) 650";
223 if (modelNum
>= 500) {
224 return "Adreno (TM) 540";
226 if (modelNum
>= 400) {
227 return "Adreno (TM) 430";
229 if (modelNum
>= 300) {
230 return "Adreno (TM) 330";
232 return "Adreno (TM) 225";
235 static const std::regex
kMali("Mali.*?([0-9][0-9]+)");
236 if (std::regex_search(str
, m
, kMali
)) {
237 const auto modelNum
= stoul(m
.str(1));
238 if (modelNum
>= 800) {
241 if (modelNum
>= 700) {
244 if (modelNum
>= 600) {
247 if (modelNum
>= 400) {
248 return "Mali-400 MP";
253 if (Contains(str
, "PowerVR")) {
254 if (Contains(str
, "Rogue")) {
255 return "PowerVR Rogue G6200";
257 return "PowerVR SGX 540";
260 if (Contains(str
, "Vivante")) return "Vivante GC1000";
261 if (Contains(str
, "VideoCore")) return "VideoCore IV HW";
262 if (Contains(str
, "Tegra")) return "NVIDIA Tegra";
266 static const std::string D3D_WARP
= "Microsoft Basic Render Driver";
267 if (Contains(str
, D3D_WARP
)) return str
;
269 gfxCriticalNote
<< "Couldn't sanitize RENDERER device: " << str
;
270 return "Generic Renderer";
275 std::string
SanitizeRenderer(const std::string
& str
) {
278 // e.g. "ANGLE (AMD, AMD Radeon(TM) Graphics Direct3D11 vs_5_0 ps_5_0,
279 // D3D11-27.20.1020.2002)"
280 static const std::regex
kReAngle(
281 "ANGLE [(]([^,]*), ([^,]*)( Direct3D[^,]*), .*[)]");
282 if (std::regex_match(str
, m
, kReAngle
)) {
283 const auto& vendor
= m
.str(1);
284 const auto& renderer
= m
.str(2);
285 const auto& d3d_suffix
= m
.str(3);
287 const auto renderer2
= ChooseDeviceReplacement(renderer
);
288 return std::string("ANGLE (") + vendor
+ ", " + renderer2
+ d3d_suffix
+
290 } else if (Contains(str
, "ANGLE")) {
291 gfxCriticalError() << "Failed to parse ANGLE renderer: " << str
;
294 static const std::regex
kReOpenglEngine("(.*) OpenGL Engine");
295 static const std::regex
kRePcieSse2("(.*)(/PCIe?/SSE2)");
296 static const std::regex
kReStandard("(.*)( [(].*[)])");
297 if (std::regex_match(str
, m
, kReOpenglEngine
)) {
298 const auto& dev
= m
.str(1);
299 const auto dev2
= ChooseDeviceReplacement(dev
);
302 if (std::regex_match(str
, m
, kRePcieSse2
)) {
303 const auto& dev
= m
.str(1);
304 const auto dev2
= ChooseDeviceReplacement(dev
);
305 return dev2
+ m
.str(2);
307 if (std::regex_match(str
, m
, kReStandard
)) {
308 const auto& dev
= m
.str(1);
309 const auto dev2
= ChooseDeviceReplacement(dev
);
313 const auto& dev
= str
;
314 const auto dev2
= ChooseDeviceReplacement(dev
);
318 }; // namespace webgl
319 }; // namespace mozilla