| /* | |
| * Copyright 2011 Google Inc. | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "src/gpu/ganesh/gl/GrGLUtil.h" | |
| #include "include/core/SkString.h" | |
| #include "include/gpu/ganesh/gl/GrGLExtensions.h" | |
| #include "include/gpu/ganesh/gl/GrGLFunctions.h" | |
| #include "include/private/SkTArray.h" | |
| #include "src/core/SkStringUtils.h" | |
| #include "src/gpu/ganesh/GrStencilSettings.h" | |
| #include <array> | |
| #include <cctype> | |
| #include <cstdio> | |
| #include <cstring> | |
| #include <tuple> | |
| #include <utility> | |
| using namespace skia_private; | |
| /////////////////////////////////////////////////////////////////////////////// | |
| #if GR_GL_LOG_CALLS | |
| bool gLogCallsGL = !!(GR_GL_LOG_CALLS_START); | |
| #endif | |
| #if GR_GL_CHECK_ERROR | |
| bool gCheckErrorGL = !!(GR_GL_CHECK_ERROR_START); | |
| #endif | |
| /////////////////////////////////////////////////////////////////////////////// | |
| GrGLStandard GrGLGetStandardInUseFromString(const char* versionString) { | |
| if (!versionString) { | |
| SkDebugf("nullptr GL version string."); | |
| return kNone_GrGLStandard; | |
| } | |
| int major, minor; | |
| // check for desktop | |
| int n = sscanf(versionString, "%d.%d", &major, &minor); | |
| if (2 == n) { | |
| return kGL_GrGLStandard; | |
| } | |
| // WebGL might look like "OpenGL ES 2.0 (WebGL 1.0 (OpenGL ES 2.0 Chromium))" | |
| int esMajor, esMinor; | |
| n = sscanf(versionString, "OpenGL ES %d.%d (WebGL %d.%d", &esMajor, &esMinor, &major, &minor); | |
| if (4 == n) { | |
| return kWebGL_GrGLStandard; | |
| } | |
| // check for ES 1 | |
| char profile[2]; | |
| n = sscanf(versionString, "OpenGL ES-%c%c %d.%d", profile, profile+1, &major, &minor); | |
| if (4 == n) { | |
| // we no longer support ES1. | |
| return kNone_GrGLStandard; | |
| } | |
| // check for ES2 | |
| n = sscanf(versionString, "OpenGL ES %d.%d", &major, &minor); | |
| if (2 == n) { | |
| return kGLES_GrGLStandard; | |
| } | |
| return kNone_GrGLStandard; | |
| } | |
| GrGLVersion GrGLGetVersionFromString(const char* versionString) { | |
| if (!versionString) { | |
| SkDebugf("nullptr GL version string."); | |
| return GR_GL_INVALID_VER; | |
| } | |
| int major, minor; | |
| // check for mesa | |
| int mesaMajor, mesaMinor; | |
| int n = sscanf(versionString, "%d.%d Mesa %d.%d", &major, &minor, &mesaMajor, &mesaMinor); | |
| if (4 == n) { | |
| return GR_GL_VER(major, minor); | |
| } | |
| n = sscanf(versionString, "%d.%d", &major, &minor); | |
| if (2 == n) { | |
| return GR_GL_VER(major, minor); | |
| } | |
| // WebGL might look like "OpenGL ES 2.0 (WebGL 1.0 (OpenGL ES 2.0 Chromium))" | |
| int esMajor, esMinor; | |
| n = sscanf(versionString, "OpenGL ES %d.%d (WebGL %d.%d", &esMajor, &esMinor, &major, &minor); | |
| if (4 == n) { | |
| return GR_GL_VER(major, minor); | |
| } | |
| char profile[2]; | |
| n = sscanf(versionString, "OpenGL ES-%c%c %d.%d", profile, profile + 1, &major, &minor); | |
| if (4 == n) { | |
| return GR_GL_VER(major, minor); | |
| } | |
| n = sscanf(versionString, "OpenGL ES %d.%d", &major, &minor); | |
| if (2 == n) { | |
| return GR_GL_VER(major, minor); | |
| } | |
| return GR_GL_INVALID_VER; | |
| } | |
| GrGLVersion GrGLGetVersion(const GrGLInterface* gl) { | |
| SkASSERT(gl); | |
| const GrGLubyte* v; | |
| GR_GL_CALL_RET(gl, v, GetString(GR_GL_VERSION)); | |
| return GrGLGetVersionFromString((const char*)v); | |
| } | |
| static GrGLSLVersion get_glsl_version(const char* versionString) { | |
| SkASSERT(versionString); | |
| int major, minor; | |
| int n = sscanf(versionString, "%d.%d", &major, &minor); | |
| if (2 == n) { | |
| return GR_GLSL_VER(major, minor); | |
| } | |
| n = sscanf(versionString, "OpenGL ES GLSL ES %d.%d", &major, &minor); | |
| if (2 == n) { | |
| return GR_GLSL_VER(major, minor); | |
| } | |
| #ifdef SK_BUILD_FOR_ANDROID | |
| // android hack until the gpu vender updates their drivers | |
| n = sscanf(versionString, "OpenGL ES GLSL %d.%d", &major, &minor); | |
| if (2 == n) { | |
| return GR_GLSL_VER(major, minor); | |
| } | |
| #endif | |
| return GR_GLSL_INVALID_VER; | |
| } | |
| static GrGLVendor get_vendor(const char* vendorString) { | |
| SkASSERT(vendorString); | |
| if (0 == strcmp(vendorString, "ARM")) { | |
| return GrGLVendor::kARM; | |
| } | |
| if (0 == strcmp(vendorString, "Google Inc.")) { | |
| return GrGLVendor::kGoogle; | |
| } | |
| if (0 == strcmp(vendorString, "Imagination Technologies")) { | |
| return GrGLVendor::kImagination; | |
| } | |
| if (0 == strncmp(vendorString, "Intel ", 6) || 0 == strcmp(vendorString, "Intel")) { | |
| return GrGLVendor::kIntel; | |
| } | |
| if (0 == strcmp(vendorString, "Qualcomm") || 0 == strcmp(vendorString, "freedreno")) { | |
| return GrGLVendor::kQualcomm; | |
| } | |
| if (0 == strcmp(vendorString, "NVIDIA Corporation")) { | |
| return GrGLVendor::kNVIDIA; | |
| } | |
| if (0 == strcmp(vendorString, "ATI Technologies Inc.")) { | |
| return GrGLVendor::kATI; | |
| } | |
| if (0 == strcmp(vendorString, "Apple")) { | |
| return GrGLVendor::kApple; | |
| } | |
| return GrGLVendor::kOther; | |
| } | |
| static GrGLRenderer get_renderer(const char* rendererString, const GrGLExtensions& extensions) { | |
| SkASSERT(rendererString); | |
| static const char kTegraStr[] = "NVIDIA Tegra"; | |
| if (0 == strncmp(rendererString, kTegraStr, std::size(kTegraStr) - 1)) { | |
| // Tegra strings are not very descriptive. We distinguish between the modern and legacy | |
| // architectures by the presence of NV_path_rendering. | |
| return extensions.has("GL_NV_path_rendering") ? GrGLRenderer::kTegra | |
| : GrGLRenderer::kTegra_PreK1; | |
| } | |
| int lastDigit; | |
| int n = sscanf(rendererString, "PowerVR SGX 54%d", &lastDigit); | |
| if (1 == n && lastDigit >= 0 && lastDigit <= 9) { | |
| return GrGLRenderer::kPowerVR54x; | |
| } | |
| if (strstr(rendererString, "PowerVR B-Series")) { | |
| return GrGLRenderer::kPowerVRBSeries; | |
| } | |
| // certain iOS devices also use PowerVR54x GPUs | |
| static const char kAppleA4Str[] = "Apple A4"; | |
| static const char kAppleA5Str[] = "Apple A5"; | |
| static const char kAppleA6Str[] = "Apple A6"; | |
| if (0 == strncmp(rendererString, kAppleA4Str, std::size(kAppleA4Str) - 1) || | |
| 0 == strncmp(rendererString, kAppleA5Str, std::size(kAppleA5Str) - 1) || | |
| 0 == strncmp(rendererString, kAppleA6Str, std::size(kAppleA6Str) - 1)) { | |
| return GrGLRenderer::kPowerVR54x; | |
| } | |
| static const char kPowerVRRogueStr[] = "PowerVR Rogue"; | |
| static const char kAppleA7Str[] = "Apple A7"; | |
| static const char kAppleA8Str[] = "Apple A8"; | |
| if (0 == strncmp(rendererString, kPowerVRRogueStr, std::size(kPowerVRRogueStr) - 1) || | |
| 0 == strncmp(rendererString, kAppleA7Str, std::size(kAppleA7Str) - 1) || | |
| 0 == strncmp(rendererString, kAppleA8Str, std::size(kAppleA8Str) - 1)) { | |
| return GrGLRenderer::kPowerVRRogue; | |
| } | |
| int adrenoNumber; | |
| n = sscanf(rendererString, "Adreno (TM) %d", &adrenoNumber); | |
| if (n < 1) { | |
| // retry with freedreno driver | |
| n = sscanf(rendererString, "FD%d", &adrenoNumber); | |
| } | |
| if (1 == n) { | |
| if (adrenoNumber >= 300) { | |
| if (adrenoNumber < 400) { | |
| return GrGLRenderer::kAdreno3xx; | |
| } | |
| if (adrenoNumber < 500) { | |
| return adrenoNumber >= 430 ? GrGLRenderer::kAdreno430 | |
| : GrGLRenderer::kAdreno4xx_other; | |
| } | |
| if (adrenoNumber < 600) { | |
| return adrenoNumber == 530 ? GrGLRenderer::kAdreno530 | |
| : GrGLRenderer::kAdreno5xx_other; | |
| } | |
| if (adrenoNumber < 700) { | |
| if (adrenoNumber == 615) { | |
| return GrGLRenderer::kAdreno615; | |
| } | |
| if (adrenoNumber == 620) { | |
| return GrGLRenderer::kAdreno620; | |
| } | |
| if (adrenoNumber == 621) { | |
| return GrGLRenderer::kAdreno621; | |
| } | |
| if (adrenoNumber == 630) { | |
| return GrGLRenderer::kAdreno630; | |
| } | |
| if (adrenoNumber == 640) { | |
| return GrGLRenderer::kAdreno640; | |
| } | |
| return GrGLRenderer::kAdreno6xx_other; | |
| } | |
| } | |
| } | |
| if (const char* intelString = strstr(rendererString, "Intel")) { | |
| // These generic strings seem to always come from Haswell: Iris 5100 or Iris Pro 5200 | |
| if (0 == strcmp("Intel Iris OpenGL Engine", intelString) || | |
| 0 == strcmp("Intel Iris Pro OpenGL Engine", intelString)) { | |
| return GrGLRenderer::kIntelHaswell; | |
| } | |
| if (strstr(intelString, "Sandybridge")) { | |
| return GrGLRenderer::kIntelSandyBridge; | |
| } | |
| if (strstr(intelString, "Bay Trail")) { | |
| return GrGLRenderer::kIntelValleyView; | |
| } | |
| // In Mesa, 'RKL' can be followed by 'Graphics', same for 'TGL' and 'ADL'. | |
| // Referenced from the following Mesa source code: | |
| // https://github.com/mesa3d/mesa/blob/master/include/pci_ids/iris_pci_ids.h | |
| if (strstr(intelString, "RKL")) { | |
| return GrGLRenderer::kIntelRocketLake; | |
| } | |
| if (strstr(intelString, "TGL")) { | |
| return GrGLRenderer::kIntelTigerLake; | |
| } | |
| // For Windows on ADL-S devices, 'AlderLake-S' might be followed by 'Intel(R)'. | |
| if (strstr(intelString, "ADL") || strstr(intelString, "AlderLake")) { | |
| return GrGLRenderer::kIntelAlderLake; | |
| } | |
| // For Windows on TGL or other ADL devices, we might only get 'Xe' from the string. | |
| // Since they are both 12th gen, we could temporarily use 'kIntelTigerLake' to cover | |
| // both TGL and ADL. | |
| if (strstr(intelString, "Xe")) { | |
| return GrGLRenderer::kIntelTigerLake; | |
| } | |
| // There are many possible intervening strings here: | |
| // 'Intel(R)' is a common prefix | |
| // 'Iris' may appear, followed by '(R)' or '(TM)' | |
| // 'Iris' can then be followed by 'Graphics', 'Pro Graphics', or 'Plus Graphics' | |
| // If 'Iris' isn't there, we might have 'HD Graphics' or 'UHD Graphics' | |
| // | |
| // In all cases, though, we end with 'Graphics ', an optional 'P', and a number, | |
| // so just skip to that and handle two cases: | |
| if (const char* intelGfxString = strstr(intelString, "Graphics")) { | |
| int intelNumber; | |
| if (sscanf(intelGfxString, "Graphics %d", &intelNumber) || | |
| sscanf(intelGfxString, "Graphics P%d", &intelNumber)) { | |
| if (intelNumber == 2000 || intelNumber == 3000) { | |
| return GrGLRenderer::kIntelSandyBridge; | |
| } | |
| if (intelNumber == 2500 || intelNumber == 4000) { | |
| return GrGLRenderer::kIntelIvyBridge; | |
| } | |
| if (intelNumber >= 4200 && intelNumber <= 5200) { | |
| return GrGLRenderer::kIntelHaswell; | |
| } | |
| if (intelNumber >= 400 && intelNumber <= 405) { | |
| return GrGLRenderer::kIntelCherryView; | |
| } | |
| if (intelNumber >= 5300 && intelNumber <= 6300) { | |
| return GrGLRenderer::kIntelBroadwell; | |
| } | |
| if (intelNumber >= 500 && intelNumber <= 505) { | |
| return GrGLRenderer::kIntelApolloLake; | |
| } | |
| if (intelNumber >= 510 && intelNumber <= 580) { | |
| return GrGLRenderer::kIntelSkyLake; | |
| } | |
| if (intelNumber >= 600 && intelNumber <= 605) { | |
| return GrGLRenderer::kIntelGeminiLake; | |
| } | |
| // 610 and 630 are reused from KabyLake to CoffeeLake. The CoffeeLake variants | |
| // are "UHD Graphics", while the KabyLake ones are "HD Graphics" | |
| if (intelNumber == 610 || intelNumber == 630) { | |
| return strstr(intelString, "UHD") ? GrGLRenderer::kIntelCoffeeLake | |
| : GrGLRenderer::kIntelKabyLake; | |
| } | |
| if (intelNumber >= 610 && intelNumber <= 650) { | |
| return GrGLRenderer::kIntelKabyLake; | |
| } | |
| if (intelNumber == 655) { | |
| return GrGLRenderer::kIntelCoffeeLake; | |
| } | |
| // 710/730/750/770 are all 12th gen UHD Graphics, but it's hard to distinguish | |
| // among RKL, TGL and ADL. We might temporarily use 'kIntelTigerLake' to cover all. | |
| if (intelNumber >= 710 && intelNumber <= 770) { | |
| return GrGLRenderer::kIntelTigerLake; | |
| } | |
| if (intelNumber >= 910 && intelNumber <= 950) { | |
| return GrGLRenderer::kIntelIceLake; | |
| } | |
| } | |
| } | |
| } | |
| // The AMD string can have a somewhat arbitrary preamble (see skbug.com/40038435) | |
| static constexpr char kRadeonStr[] = "Radeon "; | |
| if (const char* amdString = strstr(rendererString, kRadeonStr)) { | |
| amdString += strlen(kRadeonStr); | |
| // Sometimes there is a (TM) and sometimes not. | |
| static constexpr char kTMStr[] = "(TM) "; | |
| if (!strncmp(amdString, kTMStr, strlen(kTMStr))) { | |
| amdString += strlen(kTMStr); | |
| } | |
| char amd0, amd1, amd2; | |
| int amdModel; | |
| n = sscanf(amdString, "R9 M3%c%c", &amd0, &amd1); | |
| if (2 == n && isdigit(amd0) && isdigit(amd1)) { | |
| return GrGLRenderer::kAMDRadeonR9M3xx; | |
| } | |
| n = sscanf(amdString, "R9 M4%c%c", &amd0, &amd1); | |
| if (2 == n && isdigit(amd0) && isdigit(amd1)) { | |
| return GrGLRenderer::kAMDRadeonR9M4xx; | |
| } | |
| n = sscanf(amdString, "HD 7%c%c%c Series", &amd0, &amd1, &amd2); | |
| if (3 == n && isdigit(amd0) && isdigit(amd1) && isdigit(amd2)) { | |
| return GrGLRenderer::kAMDRadeonHD7xxx; | |
| } | |
| n = sscanf(amdString, "Pro 5%c%c%c", &amd0, &amd1, &amd2); | |
| if (3 == n && isdigit(amd0) && isdigit(amd1) && isdigit(amd2)) { | |
| return GrGLRenderer::kAMDRadeonPro5xxx; | |
| } | |
| n = sscanf(amdString, "Pro Vega %i", &amdModel); | |
| if (1 == n) { | |
| return GrGLRenderer::kAMDRadeonProVegaxx; | |
| } | |
| } | |
| if (strstr(rendererString, "llvmpipe")) { | |
| return GrGLRenderer::kGalliumLLVM; | |
| } | |
| if (strstr(rendererString, "Android Emulator")) { | |
| return GrGLRenderer::kAndroidEmulator; | |
| } | |
| static const char kMaliGStr[] = "Mali-G"; | |
| if (0 == strncmp(rendererString, kMaliGStr, std::size(kMaliGStr) - 1)) { | |
| return GrGLRenderer::kMaliG; | |
| } | |
| static const char kMaliTStr[] = "Mali-T"; | |
| if (0 == strncmp(rendererString, kMaliTStr, std::size(kMaliTStr) - 1)) { | |
| return GrGLRenderer::kMaliT; | |
| } | |
| int mali400Num; | |
| if (1 == sscanf(rendererString, "Mali-%d", &mali400Num) && mali400Num >= 400 && | |
| mali400Num < 500) { | |
| return GrGLRenderer::kMali4xx; | |
| } | |
| static const char kAppleStr[] = "Apple"; | |
| if (0 == strncmp(rendererString, kAppleStr, std::size(kAppleStr) - 1)) { | |
| return GrGLRenderer::kApple; | |
| } | |
| if (strstr(rendererString, "WebGL")) { | |
| return GrGLRenderer::kWebGL; | |
| } | |
| return GrGLRenderer::kOther; | |
| } | |
| static bool is_commamd_buffer(const char* rendererString, const char* versionString) { | |
| SkASSERT(rendererString); | |
| SkASSERT(versionString); | |
| int major, minor; | |
| static const char kChromium[] = "Chromium"; | |
| char suffix[std::size(kChromium)] = {0}; | |
| return (0 == strcmp(rendererString, kChromium) || | |
| (3 == sscanf(versionString, "OpenGL ES %d.%d %8s", &major, &minor, suffix) && | |
| 0 == strcmp(kChromium, suffix))); | |
| } | |
| static bool is_virgl(const char* rendererString) { | |
| return !!strstr(rendererString, "virgl"); | |
| } | |
| static std::tuple<GrGLDriver, GrGLDriverVersion> get_driver_and_version(GrGLStandard standard, | |
| GrGLVendor vendor, | |
| const char* vendorString, | |
| const char* rendererString, | |
| const char* versionString) { | |
| SkASSERT(rendererString); | |
| SkASSERT(versionString); | |
| GrGLDriver driver = GrGLDriver::kUnknown; | |
| GrGLDriverVersion driverVersion = GR_GL_DRIVER_UNKNOWN_VER; | |
| int major, minor, rev, driverMajor, driverMinor, driverPoint; | |
| // This is the same on ES and regular GL. | |
| if (!strcmp(vendorString, "freedreno")) { | |
| driver = GrGLDriver::kFreedreno; | |
| } else if (GR_IS_GR_GL(standard)) { | |
| if (vendor == GrGLVendor::kNVIDIA) { | |
| driver = GrGLDriver::kNVIDIA; | |
| int n = sscanf(versionString, | |
| "%d.%d.%d NVIDIA %d.%d", | |
| &major, | |
| &minor, | |
| &rev, | |
| &driverMajor, | |
| &driverMinor); | |
| // Some older NVIDIA drivers don't report the driver version. | |
| if (n == 5) { | |
| driverVersion = GR_GL_DRIVER_VER(driverMajor, driverMinor, 0); | |
| } | |
| } else { | |
| int n = sscanf(versionString, | |
| "%d.%d Mesa %d.%d", | |
| &major, | |
| &minor, | |
| &driverMajor, | |
| &driverMinor); | |
| if (n != 4) { | |
| n = sscanf(versionString, | |
| "%d.%d (Core Profile) Mesa %d.%d", | |
| &major, | |
| &minor, | |
| &driverMajor, | |
| &driverMinor); | |
| } | |
| if (n == 4) { | |
| driver = GrGLDriver::kMesa; | |
| driverVersion = GR_GL_DRIVER_VER(driverMajor, driverMinor, 0); | |
| } | |
| } | |
| } else if (standard == kGLES_GrGLStandard) { | |
| if (vendor == GrGLVendor::kNVIDIA) { | |
| driver = GrGLDriver::kNVIDIA; | |
| int n = sscanf(versionString, | |
| "OpenGL ES %d.%d NVIDIA %d.%d", | |
| &major, | |
| &minor, | |
| &driverMajor, | |
| &driverMinor); | |
| // Some older NVIDIA drivers don't report the driver version. | |
| if (n == 4) { | |
| driverVersion = GR_GL_DRIVER_VER(driverMajor, driverMinor, 0); | |
| } | |
| } else if (vendor == GrGLVendor::kImagination) { | |
| int revision; | |
| int n = sscanf(versionString, | |
| "OpenGL ES %d.%d build %d.%d@%d", | |
| &major, | |
| &minor, | |
| &driverMajor, | |
| &driverMinor, | |
| &revision); | |
| if (n == 5) { | |
| driver = GrGLDriver::kImagination; | |
| driverVersion = GR_GL_DRIVER_VER(driverMajor, driverMinor, 0); | |
| } | |
| } else { | |
| int n = sscanf(versionString, | |
| "OpenGL ES %d.%d Mesa %d.%d", | |
| &major, | |
| &minor, | |
| &driverMajor, | |
| &driverMinor); | |
| if (n == 4) { | |
| driver = GrGLDriver::kMesa; | |
| driverVersion = GR_GL_DRIVER_VER(driverMajor, driverMinor, 0); | |
| } | |
| } | |
| } | |
| if (driver == GrGLDriver::kUnknown) { | |
| if (vendor == GrGLVendor::kIntel) { | |
| // We presume we're on the Intel driver since it hasn't identified itself as Mesa. | |
| driver = GrGLDriver::kIntel; | |
| // This is how the macOS version strings are structured. This might be different on | |
| // different | |
| // OSes. | |
| int n = sscanf(versionString, | |
| "%d.%d INTEL-%d.%d.%d", | |
| &major, | |
| &minor, | |
| &driverMajor, | |
| &driverMinor, | |
| &driverPoint); | |
| if (n == 5) { | |
| driverVersion = GR_GL_DRIVER_VER(driverMajor, driverMinor, driverPoint); | |
| } | |
| } else if (vendor == GrGLVendor::kQualcomm) { | |
| driver = GrGLDriver::kQualcomm; | |
| int n = sscanf(versionString, | |
| "OpenGL ES %d.%d V@%d.%d", | |
| &major, | |
| &minor, | |
| &driverMajor, | |
| &driverMinor); | |
| if (n == 4) { | |
| driverVersion = GR_GL_DRIVER_VER(driverMajor, driverMinor, 0); | |
| } | |
| } else if (vendor == GrGLVendor::kImagination) { | |
| int revision; | |
| int n = sscanf(versionString, | |
| "OpenGL ES %d.%d build %d.%d@%d", | |
| &major, | |
| &minor, | |
| &driverMajor, | |
| &driverMinor, | |
| &revision); | |
| if (n == 5) { | |
| // Revision is a large number (looks like a source control revision number) that | |
| // doesn't fit into the 'patch' bits, so omit it until we need it. | |
| driverVersion = GR_GL_DRIVER_VER(driverMajor, driverMinor, 0); | |
| } | |
| } else if (vendor == GrGLVendor::kARM) { | |
| // Example: | |
| // OpenGL ES 3.2 v1.r26p0-01rel0.217d2597f6bd19b169343737782e56e3 | |
| // It's unclear how to interpret what comes between "p" and "rel". Every string we've | |
| // seen so far has "0-01" there. We ignore it for now. | |
| int ignored0; | |
| int ignored1; | |
| int n = sscanf(versionString, | |
| "OpenGL ES %d.%d v%d.r%dp%d-%drel", | |
| &major, | |
| &minor, | |
| &driverMajor, | |
| &driverMinor, | |
| &ignored0, | |
| &ignored1); | |
| if (n == 6) { | |
| driver = GrGLDriver::kARM; | |
| driverVersion = GR_GL_DRIVER_VER(driverMajor, driverMinor, 0); | |
| } | |
| } else if (vendor == GrGLVendor::kApple) { | |
| // There doesn't appear to be a minor version | |
| int n = sscanf(versionString, | |
| "%d.%d Metal - %d", | |
| &major, | |
| &minor, | |
| &driverMajor); | |
| if (n == 3) { | |
| driver = GrGLDriver::kApple; | |
| driverVersion = GR_GL_DRIVER_VER(driverMajor, 0, 0); | |
| } | |
| } else { | |
| static constexpr char kEmulatorPrefix[] = "Android Emulator OpenGL ES Translator"; | |
| if (0 == strncmp(kEmulatorPrefix, rendererString, strlen(kEmulatorPrefix))) { | |
| driver = GrGLDriver::kAndroidEmulator; | |
| } | |
| } | |
| } | |
| return {driver, driverVersion}; | |
| } | |
| // If this is detected as ANGLE then the ANGLE backend is returned along with rendererString | |
| // stripped of "ANGLE(" and ")" at the start and end, respectively. | |
| static std::tuple<GrGLANGLEBackend, SkString> get_angle_backend(const char* rendererString) { | |
| // crbug.com/1203705 ANGLE renderer will be "ANGLE (<gl-vendor>, <gl-renderer>, <gl-version>)" | |
| // on ANGLE's GL backend with related substitutions for the inner strings on other backends. | |
| static constexpr char kHeader[] = "ANGLE ("; | |
| static constexpr size_t kHeaderLength = std::size(kHeader) - 1; | |
| int rendererLength = strlen(rendererString); | |
| if (!strncmp(rendererString, kHeader, kHeaderLength) && | |
| rendererString[rendererLength - 1] == ')') { | |
| SkString innerString; | |
| innerString.set(rendererString + kHeaderLength, rendererLength - kHeaderLength - 1); | |
| if (strstr(rendererString, "Direct3D11")) { | |
| return {GrGLANGLEBackend::kD3D11, std::move(innerString)}; | |
| } else if (strstr(rendererString, "Direct3D9")) { | |
| return {GrGLANGLEBackend::kD3D9, std::move(innerString)}; | |
| } else if (strstr(rendererString, "Metal")) { | |
| return {GrGLANGLEBackend::kMetal, std::move(innerString)}; | |
| } else if (strstr(rendererString, "OpenGL")) { | |
| return {GrGLANGLEBackend::kOpenGL, std::move(innerString)}; | |
| } else if (strstr(rendererString, "Vulkan")) { | |
| return {GrGLANGLEBackend::kVulkan, std::move(innerString)}; | |
| } | |
| } | |
| return {GrGLANGLEBackend::kUnknown, {}}; | |
| } | |
| static std::tuple<GrGLVendor, GrGLRenderer, GrGLDriver, GrGLDriverVersion> | |
| get_angle_gl_vendor_and_renderer( | |
| const char* innerString, | |
| const GrGLExtensions& extensions) { | |
| TArray<SkString> parts; | |
| SkStrSplit(innerString, ",", &parts); | |
| // This would need some fixing if we have substrings that contain commas. | |
| if (parts.size() != 3) { | |
| return {GrGLVendor::kOther, | |
| GrGLRenderer::kOther, | |
| GrGLDriver::kUnknown, | |
| GR_GL_DRIVER_UNKNOWN_VER}; | |
| } | |
| const char* angleVendorString = parts[0].c_str(); | |
| const char* angleRendererString = parts[1].c_str() + 1; // skip initial space | |
| const char* angleVersionString = parts[2].c_str() + 1; // skip initial space | |
| GrGLVendor angleVendor = get_vendor(angleVendorString); | |
| auto [angleDriver, angleDriverVersion] = get_driver_and_version(kGLES_GrGLStandard, | |
| angleVendor, | |
| angleVendorString, | |
| angleRendererString, | |
| angleVersionString); | |
| auto angleRenderer = get_renderer(angleRendererString, extensions); | |
| return {angleVendor, angleRenderer, angleDriver, angleDriverVersion}; | |
| } | |
| static GrGLVendor get_angle_metal_vendor(const char* innerString) { | |
| if (strstr(innerString, "Intel")) { | |
| return GrGLVendor::kIntel; | |
| } | |
| return GrGLVendor::kOther; | |
| } | |
| static GrGLVendor get_angle_vulkan_vendor(const char* innerString) { | |
| if (strstr(innerString, "ARM")) { | |
| return GrGLVendor::kARM; | |
| } | |
| return GrGLVendor::kOther; | |
| } | |
| static std::tuple<GrGLVendor, GrGLRenderer, GrGLDriver, GrGLDriverVersion> | |
| get_angle_d3d_vendor_and_renderer(const char* innerString) { | |
| auto vendor = GrGLVendor::kOther; | |
| auto renderer = GrGLRenderer::kOther; | |
| if (strstr(innerString, "Intel")) { | |
| vendor = GrGLVendor::kIntel; | |
| const char* modelStr; | |
| int modelNumber; | |
| if ((modelStr = strstr(innerString, "HD Graphics")) && | |
| (1 == sscanf(modelStr, "HD Graphics %i", &modelNumber) || | |
| 1 == sscanf(modelStr, "HD Graphics P%i", &modelNumber))) { | |
| switch (modelNumber) { | |
| case 2000: | |
| case 3000: | |
| renderer = GrGLRenderer::kIntelSandyBridge; | |
| break; | |
| case 4000: | |
| case 2500: | |
| renderer = GrGLRenderer::kIntelSandyBridge; | |
| break; | |
| case 510: | |
| case 515: | |
| case 520: | |
| case 530: | |
| renderer = GrGLRenderer::kIntelSkyLake; | |
| break; | |
| } | |
| } else if ((modelStr = strstr(innerString, "Iris")) && | |
| (1 == sscanf(modelStr, "Iris(TM) Graphics %i", &modelNumber) || | |
| 1 == sscanf(modelStr, "Iris(TM) Pro Graphics %i", &modelNumber) || | |
| 1 == sscanf(modelStr, "Iris(TM) Pro Graphics P%i", &modelNumber))) { | |
| switch (modelNumber) { | |
| case 540: | |
| case 550: | |
| case 555: | |
| case 580: | |
| renderer = GrGLRenderer::kIntelSkyLake; | |
| break; | |
| } | |
| } | |
| } else if (strstr(innerString, "NVIDIA")) { | |
| vendor = GrGLVendor::kNVIDIA; | |
| } else if (strstr(innerString, "Radeon")) { | |
| vendor = GrGLVendor::kATI; | |
| } | |
| // We haven't had a need yet to parse the D3D driver string. | |
| return {vendor, renderer, GrGLDriver::kUnknown, GR_GL_DRIVER_UNKNOWN_VER}; | |
| } | |
| static std::tuple<GrGLVendor, GrGLRenderer> | |
| get_webgl_vendor_and_renderer( | |
| const GrGLInterface* interface) { | |
| if (!interface->fExtensions.has("WEBGL_debug_renderer_info")) { | |
| return {GrGLVendor::kOther, | |
| GrGLRenderer::kOther}; | |
| } | |
| auto getString = [&](GrGLenum s) { | |
| const GrGLubyte* bytes = interface->fFunctions.fGetString(s); | |
| if (!bytes) { | |
| return ""; | |
| } | |
| return reinterpret_cast<const char*>(bytes); | |
| }; | |
| const char* webglVendorString = getString(GR_UNMASKED_VENDOR_WEBGL); | |
| const char* webglRendererString = getString(GR_UNMASKED_RENDERER_WEBGL); | |
| GrGLVendor webglVendor = get_vendor(webglVendorString); | |
| GrGLRenderer webglRenderer = get_renderer(webglRendererString, interface->fExtensions); | |
| if (webglVendor == GrGLVendor::kOther && strstr(webglRendererString, "Intel")) { | |
| webglVendor = GrGLVendor::kIntel; | |
| } | |
| return {webglVendor, webglRenderer}; | |
| } | |
| GrGLDriverInfo GrGLGetDriverInfo(const GrGLInterface* interface) { | |
| if (!interface) { | |
| return {}; | |
| } | |
| SkASSERT(interface->fStandard != kNone_GrGLStandard); | |
| GrGLDriverInfo info; | |
| info.fStandard = interface->fStandard; | |
| auto getString = [&](GrGLenum s) { | |
| const GrGLubyte* bytes = interface->fFunctions.fGetString(s); | |
| if (!bytes) { | |
| return ""; | |
| } | |
| return reinterpret_cast<const char*>(bytes); | |
| }; | |
| const char* const version = getString(GR_GL_VERSION); | |
| const char* const slversion = getString(GR_GL_SHADING_LANGUAGE_VERSION); | |
| const char* const renderer = getString(GR_GL_RENDERER); | |
| const char* const vendor = getString(GR_GL_VENDOR); | |
| info.fVersion = GrGLGetVersionFromString(version); | |
| info.fGLSLVersion = get_glsl_version(slversion); | |
| info.fVendor = get_vendor(vendor); | |
| info.fRenderer = get_renderer(renderer, interface->fExtensions); | |
| std::tie(info.fDriver, info.fDriverVersion) = get_driver_and_version(interface->fStandard, | |
| info.fVendor, | |
| vendor, | |
| renderer, | |
| version); | |
| SkString innerAngleRendererString; | |
| std::tie(info.fANGLEBackend, innerAngleRendererString) = get_angle_backend(renderer); | |
| if (info.fANGLEBackend == GrGLANGLEBackend::kD3D9 || | |
| info.fANGLEBackend == GrGLANGLEBackend::kD3D11) { | |
| std::tie(info.fANGLEVendor, | |
| info.fANGLERenderer, | |
| info.fANGLEDriver, | |
| info.fANGLEDriverVersion) = | |
| get_angle_d3d_vendor_and_renderer(innerAngleRendererString.c_str()); | |
| } else if (info.fANGLEBackend == GrGLANGLEBackend::kOpenGL) { | |
| std::tie(info.fANGLEVendor, | |
| info.fANGLERenderer, | |
| info.fANGLEDriver, | |
| info.fANGLEDriverVersion) = | |
| get_angle_gl_vendor_and_renderer(innerAngleRendererString.c_str(), | |
| interface->fExtensions); | |
| } else if (info.fANGLEBackend == GrGLANGLEBackend::kMetal) { | |
| info.fANGLEVendor = get_angle_metal_vendor(innerAngleRendererString.c_str()); | |
| } else if (info.fANGLEBackend == GrGLANGLEBackend::kVulkan) { | |
| info.fANGLEVendor = get_angle_vulkan_vendor(innerAngleRendererString.c_str()); | |
| } | |
| if (info.fRenderer == GrGLRenderer::kWebGL) { | |
| std::tie(info.fWebGLVendor, | |
| info.fWebGLRenderer) = | |
| get_webgl_vendor_and_renderer(interface); | |
| } | |
| info.fIsOverCommandBuffer = is_commamd_buffer(renderer, version); | |
| info.fIsRunningOverVirgl = is_virgl(renderer); | |
| return info; | |
| } | |
| GrGLenum GrToGLStencilFunc(GrStencilTest test) { | |
| static constexpr std::array<GrGLenum, kGrStencilTestCount> gTable = { | |
| GR_GL_ALWAYS, // kAlways | |
| GR_GL_NEVER, // kNever | |
| GR_GL_GREATER, // kGreater | |
| GR_GL_GEQUAL, // kGEqual | |
| GR_GL_LESS, // kLess | |
| GR_GL_LEQUAL, // kLEqual | |
| GR_GL_EQUAL, // kEqual | |
| GR_GL_NOTEQUAL, // kNotEqual | |
| }; | |
| static_assert(0 == (int)GrStencilTest::kAlways); | |
| static_assert(1 == (int)GrStencilTest::kNever); | |
| static_assert(2 == (int)GrStencilTest::kGreater); | |
| static_assert(3 == (int)GrStencilTest::kGEqual); | |
| static_assert(4 == (int)GrStencilTest::kLess); | |
| static_assert(5 == (int)GrStencilTest::kLEqual); | |
| static_assert(6 == (int)GrStencilTest::kEqual); | |
| static_assert(7 == (int)GrStencilTest::kNotEqual); | |
| SkASSERT(test < (GrStencilTest)kGrStencilTestCount); | |
| return gTable[(int)test]; | |
| } | |
| bool GrGLFormatIsCompressed(GrGLFormat format) { | |
| switch (format) { | |
| case GrGLFormat::kCOMPRESSED_ETC1_RGB8: | |
| case GrGLFormat::kCOMPRESSED_RGB8_ETC2: | |
| case GrGLFormat::kCOMPRESSED_RGB8_BC1: | |
| case GrGLFormat::kCOMPRESSED_RGBA8_BC1: | |
| return true; | |
| case GrGLFormat::kRGBA8: | |
| case GrGLFormat::kR8: | |
| case GrGLFormat::kALPHA8: | |
| case GrGLFormat::kLUMINANCE8: | |
| case GrGLFormat::kLUMINANCE8_ALPHA8: | |
| case GrGLFormat::kBGRA8: | |
| case GrGLFormat::kRGB565: | |
| case GrGLFormat::kRGBA16F: | |
| case GrGLFormat::kR16F: | |
| case GrGLFormat::kLUMINANCE16F: | |
| case GrGLFormat::kRGB8: | |
| case GrGLFormat::kRGBX8: | |
| case GrGLFormat::kRG8: | |
| case GrGLFormat::kRGB10_A2: | |
| case GrGLFormat::kRGBA4: | |
| case GrGLFormat::kSRGB8_ALPHA8: | |
| case GrGLFormat::kR16: | |
| case GrGLFormat::kRG16: | |
| case GrGLFormat::kRGBA16: | |
| case GrGLFormat::kRG16F: | |
| case GrGLFormat::kSTENCIL_INDEX8: | |
| case GrGLFormat::kSTENCIL_INDEX16: | |
| case GrGLFormat::kDEPTH24_STENCIL8: | |
| case GrGLFormat::kUnknown: | |
| return false; | |
| } | |
| SkUNREACHABLE; | |
| } |