/* * Cogl * * An object oriented GL/GLES Abstraction/Utility Layer * * Copyright (C) 2012 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "cogl-gpu-info-private.h" #include "cogl-context-private.h" #include "cogl-version.h" typedef struct { const char *renderer_string; const char *version_string; const char *vendor_string; } CoglGpuInfoStrings; typedef struct CoglGpuInfoArchitectureDescription { CoglGpuInfoArchitecture architecture; const char *name; CoglGpuInfoArchitectureFlag flags; CoglBool (* check_function) (const CoglGpuInfoStrings *strings); } CoglGpuInfoArchitectureDescription; typedef struct { CoglGpuInfoVendor vendor; const char *name; CoglBool (* check_function) (const CoglGpuInfoStrings *strings); const CoglGpuInfoArchitectureDescription *architectures; } CoglGpuInfoVendorDescription; typedef struct { CoglGpuInfoDriverPackage driver_package; const char *name; CoglBool (* check_function) (const CoglGpuInfoStrings *strings, int *version_out); } CoglGpuInfoDriverPackageDescription; static CoglBool _cogl_gpu_info_parse_version_string (const char *version_string, int n_components, const char **tail, int *version_ret) { int version = 0; uint64_t part; int i; for (i = 0; ; i++) { errno = 0; part = g_ascii_strtoull (version_string, (char **) &version_string, 10); if (errno || part > COGL_VERSION_MAX_COMPONENT_VALUE) return FALSE; version |= part << ((2 - i) * COGL_VERSION_COMPONENT_BITS); if (i + 1 >= n_components) break; if (*version_string != '.') return FALSE; version_string++; } if (version_ret) *version_ret = version; if (tail) *tail = version_string; return TRUE; } static CoglBool match_phrase (const char *string, const char *phrase) { const char *part = strstr (string, phrase); int len; if (part == NULL) return FALSE; /* The match must either be at the beginning of the string or preceded by a space. */ if (part > string && part[-1] != ' ') return FALSE; /* Also match must either be at end of string or followed by a * space. */ len = strlen (phrase); if (part[len] != '\0' && part[len] != ' ') return FALSE; return TRUE; } static CoglBool check_intel_vendor (const CoglGpuInfoStrings *strings) { return match_phrase (strings->renderer_string, "Intel(R)"); } static CoglBool check_imagination_technologies_vendor (const CoglGpuInfoStrings *strings) { if (strcmp (strings->vendor_string, "Imagination Technologies") != 0) return FALSE; return TRUE; } static CoglBool check_arm_vendor (const CoglGpuInfoStrings *strings) { if (strcmp (strings->vendor_string, "ARM") != 0) return FALSE; return TRUE; } static CoglBool check_qualcomm_vendor (const CoglGpuInfoStrings *strings) { if (strcmp (strings->vendor_string, "Qualcomm") != 0) return FALSE; return TRUE; } static CoglBool check_nvidia_vendor (const CoglGpuInfoStrings *strings) { if (strcmp (strings->vendor_string, "NVIDIA") != 0) return FALSE; return TRUE; } static CoglBool check_ati_vendor (const CoglGpuInfoStrings *strings) { if (strcmp (strings->vendor_string, "ATI") != 0) return FALSE; return TRUE; } static CoglBool check_mesa_vendor (const CoglGpuInfoStrings *strings) { if (strcmp (strings->vendor_string, "Tungsten Graphics, Inc") == 0) return TRUE; else if (strcmp (strings->vendor_string, "VMware, Inc.") == 0) return TRUE; else if (strcmp (strings->vendor_string, "Mesa Project") == 0) return TRUE; return FALSE; } static CoglBool check_true (const CoglGpuInfoStrings *strings) { /* This is a last resort so it always matches */ return TRUE; } static CoglBool check_sandybridge_architecture (const CoglGpuInfoStrings *strings) { return match_phrase (strings->renderer_string, "Sandybridge"); } static CoglBool check_llvmpipe_architecture (const CoglGpuInfoStrings *strings) { return match_phrase (strings->renderer_string, "llvmpipe"); } static CoglBool check_softpipe_architecture (const CoglGpuInfoStrings *strings) { return match_phrase (strings->renderer_string, "softpipe"); } static CoglBool check_swrast_architecture (const CoglGpuInfoStrings *strings) { return match_phrase (strings->renderer_string, "software rasterizer") || match_phrase (strings->renderer_string, "Software Rasterizer"); } static CoglBool check_sgx_architecture (const CoglGpuInfoStrings *strings) { if (strncmp (strings->renderer_string, "PowerVR SGX", 12) != 0) return FALSE; return TRUE; } static CoglBool check_mali_architecture (const CoglGpuInfoStrings *strings) { if (strncmp (strings->renderer_string, "Mali-", 5) != 0) return FALSE; return TRUE; } static const CoglGpuInfoArchitectureDescription intel_architectures[] = { { COGL_GPU_INFO_ARCHITECTURE_SANDYBRIDGE, "Sandybridge", COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_IMMEDIATE_MODE | COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_IMMEDIATE_MODE, check_sandybridge_architecture }, { COGL_GPU_INFO_ARCHITECTURE_UNKNOWN, "Unknown", COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_IMMEDIATE_MODE | COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_IMMEDIATE_MODE, check_true } }; static const CoglGpuInfoArchitectureDescription powervr_architectures[] = { { COGL_GPU_INFO_ARCHITECTURE_SGX, "SGX", COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_TILED | COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_DEFERRED, check_sgx_architecture }, { COGL_GPU_INFO_ARCHITECTURE_UNKNOWN, "Unknown", COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_TILED | COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_TILED, check_true } }; static const CoglGpuInfoArchitectureDescription arm_architectures[] = { { COGL_GPU_INFO_ARCHITECTURE_MALI, "Mali", COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_TILED | COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_IMMEDIATE_MODE, check_mali_architecture }, { COGL_GPU_INFO_ARCHITECTURE_UNKNOWN, "Unknown", COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_TILED | COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_IMMEDIATE_MODE, check_true } }; static const CoglGpuInfoArchitectureDescription mesa_architectures[] = { { COGL_GPU_INFO_ARCHITECTURE_LLVMPIPE, "LLVM Pipe", COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_IMMEDIATE_MODE | COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_SOFTWARE | COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_IMMEDIATE_MODE | COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_SOFTWARE, check_llvmpipe_architecture }, { COGL_GPU_INFO_ARCHITECTURE_SOFTPIPE, "Softpipe", COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_IMMEDIATE_MODE | COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_SOFTWARE | COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_IMMEDIATE_MODE | COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_SOFTWARE, check_softpipe_architecture }, { COGL_GPU_INFO_ARCHITECTURE_SWRAST, "SWRast", COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_IMMEDIATE_MODE | COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_SOFTWARE | COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_IMMEDIATE_MODE | COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_SOFTWARE, check_swrast_architecture }, { COGL_GPU_INFO_ARCHITECTURE_UNKNOWN, "Unknown", COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_IMMEDIATE_MODE | COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_IMMEDIATE_MODE, check_true } }; static const CoglGpuInfoArchitectureDescription unknown_architectures[] = { { COGL_GPU_INFO_ARCHITECTURE_UNKNOWN, "Unknown", COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_IMMEDIATE_MODE | COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_IMMEDIATE_MODE, check_true } }; static const CoglGpuInfoVendorDescription _cogl_gpu_info_vendors[] = { { COGL_GPU_INFO_VENDOR_INTEL, "Intel", check_intel_vendor, intel_architectures }, { COGL_GPU_INFO_VENDOR_IMAGINATION_TECHNOLOGIES, "Imagination Technologies", check_imagination_technologies_vendor, powervr_architectures }, { COGL_GPU_INFO_VENDOR_ARM, "ARM", check_arm_vendor, arm_architectures }, { COGL_GPU_INFO_VENDOR_QUALCOMM, "Qualcomm", check_qualcomm_vendor, unknown_architectures }, { COGL_GPU_INFO_VENDOR_NVIDIA, "Nvidia", check_nvidia_vendor, unknown_architectures }, { COGL_GPU_INFO_VENDOR_ATI, "ATI", check_ati_vendor, unknown_architectures }, /* Must be last */ { COGL_GPU_INFO_VENDOR_MESA, "Mesa", check_mesa_vendor, mesa_architectures }, { COGL_GPU_INFO_VENDOR_UNKNOWN, "Unknown", check_true, unknown_architectures } }; static CoglBool check_mesa_driver_package (const CoglGpuInfoStrings *strings, int *version_ret) { uint64_t micro_part; const char *v; /* The version string should always begin a two-part GL version number */ if (!_cogl_gpu_info_parse_version_string (strings->version_string, 2, /* n_components */ &v, /* tail */ NULL /* version_ret */)) return FALSE; /* In mesa this will be followed optionally by "(Core Profile)" and * then "Mesa" */ v = strstr (v, " Mesa "); if (!v) return FALSE; v += 6; /* Next there will be a version string that is at least two components. On a git devel build the version will be something like "-devel" instead */ if (!_cogl_gpu_info_parse_version_string (v, 2, /* n_components */ &v, /* tail */ version_ret)) return FALSE; /* If it is a development build then we'll just leave the micro number as 0 */ if (g_str_has_prefix (v, "-devel")) return TRUE; /* Otherwise there should be a micro version number */ if (*v != '.') return FALSE; errno = 0; micro_part = g_ascii_strtoull (v + 1, NULL /* endptr */, 10 /* base */); if (errno || micro_part > COGL_VERSION_MAX_COMPONENT_VALUE) return FALSE; *version_ret = COGL_VERSION_ENCODE (COGL_VERSION_GET_MAJOR (*version_ret), COGL_VERSION_GET_MINOR (*version_ret), micro_part); return TRUE; } UNIT_TEST (check_mesa_driver_package_parser, 0, /* no requirements */ 0 /* no failure cases */) { const CoglGpuInfoStrings test_strings[] = { { .version_string = "3.1 Mesa 9.2-devel15436ad" }, { .version_string = "3.1 (Core Profile) Mesa 9.2.0-devel (git-15436ad)" } }; int i; int version; for (i = 0; i < G_N_ELEMENTS (test_strings); i++) { g_assert (check_mesa_driver_package (&test_strings[i], &version)); g_assert_cmpint (version, ==, COGL_VERSION_ENCODE (9, 2, 0)); } } static CoglBool check_unknown_driver_package (const CoglGpuInfoStrings *strings, int *version_out) { *version_out = 0; /* This is a last resort so it always matches */ return TRUE; } static const CoglGpuInfoDriverPackageDescription _cogl_gpu_info_driver_packages[] = { { COGL_GPU_INFO_DRIVER_PACKAGE_MESA, "Mesa", check_mesa_driver_package }, /* Must be last */ { COGL_GPU_INFO_DRIVER_PACKAGE_UNKNOWN, "Unknown", check_unknown_driver_package } }; void _cogl_gpu_info_init (CoglContext *ctx, CoglGpuInfo *gpu) { CoglGpuInfoStrings strings; int i; strings.renderer_string = (const char *) ctx->glGetString (GL_RENDERER); strings.version_string = _cogl_context_get_gl_version (ctx); strings.vendor_string = (const char *) ctx->glGetString (GL_VENDOR); /* Determine the driver package */ for (i = 0; ; i++) { const CoglGpuInfoDriverPackageDescription *description = _cogl_gpu_info_driver_packages + i; if (description->check_function (&strings, &gpu->driver_package_version)) { gpu->driver_package = description->driver_package; gpu->driver_package_name = description->name; break; } } /* Determine the GPU vendor */ for (i = 0; ; i++) { const CoglGpuInfoVendorDescription *description = _cogl_gpu_info_vendors + i; if (description->check_function (&strings)) { int j; gpu->vendor = description->vendor; gpu->vendor_name = description->name; for (j = 0; ; j++) { const CoglGpuInfoArchitectureDescription *architecture = description->architectures + j; if (architecture->check_function (&strings)) { gpu->architecture = architecture->architecture; gpu->architecture_name = architecture->name; gpu->architecture_flags = architecture->flags; goto probed; } } } } probed: COGL_NOTE (WINSYS, "Driver package = %s, vendor = %s, architecture = %s\n", gpu->driver_package_name, gpu->vendor_name, gpu->architecture_name); /* Determine the driver bugs */ /* In Mesa the glReadPixels implementation is really slow when using the Intel driver. The Intel driver has a fast blit path when reading into a PBO. Reading into a temporary PBO and then memcpying back out to the application's memory is faster than a regular glReadPixels in this case */ if (gpu->vendor == COGL_GPU_INFO_VENDOR_INTEL && gpu->driver_package == COGL_GPU_INFO_DRIVER_PACKAGE_MESA) gpu->driver_bugs |= COGL_GPU_INFO_DRIVER_BUG_MESA_46631_SLOW_READ_PIXELS; }