detect-gpu
Version:
Classify GPU's based on their benchmark score in order to provide an adaptive experience.
197 lines (160 loc) • 6.29 kB
JavaScript
// Data
import { BENCHMARK_SCORE_DESKTOP, BENCHMARK_SCORE_MOBILE } from './benchmark';
// Device
import Device from './device';
// Utilities
import { getBenchmarkByPercentage, getWebGLUnmaskedRenderer } from './utilities';
// Instantiate device detection
const device = new Device();
function cleanEntryString(entryString) {
return entryString
.toLowerCase() // Lowercase all for easier matching
.split('- ')[1] // Remove prelude score (`3 - `)
.split(' /')[0]; // Reduce 'apple a9x / powervr series 7xt' to 'apple a9x'
}
function getEntryVersionNumber(entryString) {
return entryString.replace(/[\D]/g, ''); // Grab and concat all digits in the string
}
function cleanRendererString(rendererString) {
let cleanedRendererString = rendererString.toLowerCase();
// Strip off ANGLE and Direct3D version
if (cleanedRendererString.includes('angle (') && cleanedRendererString.includes('direct3d')) {
cleanedRendererString = cleanedRendererString.replace('angle (', '').split(' direct3d')[0];
}
// Strip off the GB amount (1060 6gb was being concatenated to 10606 and because of it using the fallback)
if (cleanedRendererString.includes('nvidia') && cleanedRendererString.includes('gb')) {
cleanedRendererString = cleanedRendererString.split(/\dgb/)[0];
}
return cleanedRendererString;
}
class GPUTier {
constructor(options = {}) {
this.mobileBenchmarkPercentages = [10, 40, 30, 20]; // [TIER_0, TIER_1, TIER_2, TIER_3]
this.desktopBenchmarkPercentages = [10, 40, 30, 20]; // [TIER_0, TIER_1, TIER_2, TIER_3]
this.forceRendererString = false;
this.forceMobile = false;
Object.assign(this, options);
const isMobile = device.mobile || device.tablet || this.forceMobile;
const isDesktop = !isMobile;
let renderer;
let tier;
let type;
if (this.forceRendererString === false) {
renderer = getWebGLUnmaskedRenderer();
} else {
renderer = this.forceRendererString;
}
// WebGL support is missing
if (!renderer) {
if (isMobile) {
return {
tier: 'GPU_MOBILE_TIER_0',
type: 'WEBGL_UNSUPPORTED',
};
}
return {
tier: 'GPU_DESKTOP_TIER_0',
type: 'WEBGL_UNSUPPORTED',
};
}
renderer = cleanRendererString(renderer);
const rendererVersionNumber = renderer.replace(/[\D]/g, '');
// GPU BLACKLIST
// https://wiki.mozilla.org/Blocklisting/Blocked_Graphics_Drivers
// https://www.khronos.org/webgl/wiki/BlacklistsAndWhitelists
// https://chromium.googlesource.com/chromium/src/+/master/gpu/config/software_rendering_list.json
// https://chromium.googlesource.com/chromium/src/+/master/gpu/config/gpu_driver_bug_list.json
const isGPUBlacklisted = /(radeon hd 6970m|radeon hd 6770m|radeon hd 6490m|radeon hd 6630m|radeon hd 6750m|radeon hd 5750|radeon hd 5670|radeon hd 4850|radeon hd 4870|radeon hd 4670|geforce 9400m|geforce 320m|geforce 330m|geforce gt 130|geforce gt 120|geforce gtx 285|geforce 8600|geforce 9600m|geforce 9400m|geforce 8800 gs|geforce 8800 gt|quadro fx 5|quadro fx 4|radeon hd 2600|radeon hd 2400|radeon hd 2600|mali-4|mali-3|mali-2)/.test(
renderer,
);
if (isGPUBlacklisted) {
if (isMobile) {
return {
tier: 'GPU_MOBILE_TIER_0',
type: 'BLACKLISTED',
};
}
return {
tier: 'GPU_DESKTOP_TIER_0',
type: 'BLACKLISTED',
};
}
if (isMobile) {
const mobileBenchmark = getBenchmarkByPercentage(
BENCHMARK_SCORE_MOBILE,
this.mobileBenchmarkPercentages,
);
const isRendererAdreno = renderer.includes('adreno');
const isRendererApple = renderer.includes('apple');
const isRendererMali = renderer.includes('mali') && !renderer.includes('mali-t');
const isRendererMaliT = renderer.includes('mali-t');
const isRendererNVIDIA = renderer.includes('nvidia');
const isRendererPowerVR = renderer.includes('powervr');
mobileBenchmark.forEach((benchmarkTier, index) => benchmarkTier.forEach((benchmarkEntry) => {
const entry = cleanEntryString(benchmarkEntry);
const entryVersionNumber = getEntryVersionNumber(entry);
if (
(entry.includes('adreno') && isRendererAdreno)
|| (entry.includes('apple') && isRendererApple)
|| (entry.includes('mali') && !entry.includes('mali-t') && isRendererMali)
|| (entry.includes('mali-t') && isRendererMaliT)
|| (entry.includes('nvidia') && isRendererNVIDIA)
|| (entry.includes('powervr') && isRendererPowerVR)
) {
if (entryVersionNumber.includes(rendererVersionNumber)) {
tier = `GPU_MOBILE_TIER_${index}`;
type = `BENCHMARK - ${entry}`;
}
// Handle mobile edge cases
}
}));
if (!tier) {
tier = 'GPU_MOBILE_TIER_1';
type = 'FALLBACK';
}
return {
tier,
type,
};
}
if (isDesktop) {
const desktopBenchmark = getBenchmarkByPercentage(
BENCHMARK_SCORE_DESKTOP,
this.desktopBenchmarkPercentages,
);
const isRendererIntel = renderer.includes('intel');
const isRendererAMD = renderer.includes('amd');
const isRendererNVIDIA = renderer.includes('nvidia');
desktopBenchmark.forEach((benchmarkTier, index) => benchmarkTier.forEach((benchmarkEntry) => {
const entry = cleanEntryString(benchmarkEntry);
const entryVersionNumber = getEntryVersionNumber(entry);
if (
(entry.includes('intel') && isRendererIntel)
|| (entry.includes('amd') && isRendererAMD)
|| (entry.includes('nvidia') && isRendererNVIDIA)
) {
if (entryVersionNumber.includes(rendererVersionNumber)) {
tier = `GPU_DESKTOP_TIER_${index}`;
type = `BENCHMARK - ${entry}`;
}
// Handle desktop edge cases
}
}));
if (!tier) {
tier = 'GPU_DESKTOP_TIER_1';
type = 'FALLBACK';
}
return {
tier,
type,
};
}
return {
tier,
type,
};
}
}
export function getGPUTier(options = {}) {
return new GPUTier(options);
}