| // Copyright 2020 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "host-common/opengl/gpuinfo.h" |
| |
| #include "aemu/base/ArraySize.h" |
| #include "aemu/base/system/System.h" |
| #include "aemu/base/threads/FunctorThread.h" |
| #include "host-common/opengl/NativeGpuInfo.h" |
| |
| #include <assert.h> |
| #include <inttypes.h> |
| #include <sstream> |
| #include <utility> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| using android::base::arraySize; |
| using android::base::FunctorThread; |
| |
| // Try to switch to NVIDIA on Optimus systems, |
| // and AMD GPU on AmdPowerXpress. |
| // See http://developer.download.nvidia.com/devzone/devcenter/gamegraphics/files/OptimusRenderingPolicies.pdf |
| // and https://community.amd.com/thread/169965 |
| // These variables need to be visible from the final emulator executable |
| // as exported symbols. |
| #ifdef _WIN32 |
| #define FLAG_EXPORT __declspec(dllexport) |
| #else |
| #define FLAG_EXPORT __attribute__ ((visibility ("default"))) |
| #endif |
| |
| extern "C" { |
| |
| FLAG_EXPORT int NvOptimusEnablement = 0x00000001; |
| FLAG_EXPORT int AmdPowerXpressRequestHighPerformance = 0x00000001; |
| |
| } // extern "C" |
| |
| #undef FLAG_EXPORT |
| |
| |
| void GpuInfo::addDll(std::string dll_str) { |
| dlls.push_back(std::move(dll_str)); |
| } |
| |
| void GpuInfoList::addGpu() { |
| infos.push_back(GpuInfo()); |
| } |
| GpuInfo& GpuInfoList::currGpu() { |
| if (infos.empty()) { addGpu(); } |
| return infos.back(); |
| } |
| |
| std::string GpuInfoList::dump() const { |
| std::stringstream ss; |
| for (unsigned int i = 0; i < infos.size(); i++) { |
| ss << "GPU #" << i + 1 << std::endl; |
| |
| if (!infos[i].make.empty()) { |
| ss << " Make: " << infos[i].make << std::endl; |
| } |
| if (!infos[i].model.empty()) { |
| ss << " Model: " << infos[i].model << std::endl; |
| } |
| if (!infos[i].device_id.empty()) { |
| ss << " Device ID: " << infos[i].device_id << std::endl; |
| } |
| if (!infos[i].revision_id.empty()) { |
| ss << " Revision ID: " << infos[i].revision_id << std::endl; |
| } |
| if (!infos[i].version.empty()) { |
| ss << " Driver version: " << infos[i].version << std::endl; |
| } |
| if (!infos[i].renderer.empty()) { |
| ss << " Renderer: " << infos[i].renderer << std::endl; |
| } |
| } |
| return ss.str(); |
| } |
| |
| void GpuInfoList::clear() { |
| blacklist_status = false; |
| Anglelist_status = false; |
| SyncBlacklist_status = false; |
| VulkanBlacklist_status = false; |
| infos.clear(); |
| } |
| |
| static bool gpuinfo_query_list(GpuInfoList* gpulist, |
| const BlacklistEntry* list, |
| int size) { |
| for (auto gpuinfo : gpulist->infos) { |
| for (int i = 0; i < size; i++) { |
| auto bl_entry = list[i]; |
| const char* bl_make = bl_entry.make; |
| const char* bl_model = bl_entry.model; |
| const char* bl_device_id = bl_entry.device_id; |
| const char* bl_revision_id = bl_entry.revision_id; |
| const char* bl_version = bl_entry.version; |
| const char* bl_renderer = bl_entry.renderer; |
| const char* bl_os = bl_entry.os; |
| |
| if (bl_make && (gpuinfo.make != bl_make)) |
| continue; |
| if (bl_model && (gpuinfo.model != bl_model)) |
| continue; |
| if (bl_device_id && (gpuinfo.device_id != bl_device_id)) |
| continue; |
| if (bl_revision_id && (gpuinfo.revision_id != bl_revision_id)) |
| continue; |
| if (bl_version && (gpuinfo.revision_id != bl_version)) |
| continue; |
| if (bl_renderer && (gpuinfo.renderer.find(bl_renderer) == |
| std::string::npos)) |
| continue; |
| if (bl_os && (gpuinfo.os != bl_os)) |
| continue; |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| // Actual blacklist starts here. |
| // Most entries imported from Chrome blacklist. |
| static const BlacklistEntry sGpuBlacklist[] = { |
| |
| // Make | Model | DeviceID | RevisionID | DriverVersion | Renderer | |
| // OS |
| {nullptr, nullptr, "0x7249", nullptr, nullptr, |
| nullptr, "M"}, // ATI Radeon X1900 on Mac |
| {"8086", nullptr, nullptr, nullptr, nullptr, |
| "Mesa", "L"}, // Linux, Intel, Mesa |
| {"8086", nullptr, nullptr, nullptr, nullptr, |
| "mesa", "L"}, // Linux, Intel, Mesa |
| |
| {"8086", nullptr, "27ae", nullptr, nullptr, |
| nullptr, nullptr}, // Intel 945 Chipset |
| {"1002", nullptr, nullptr, nullptr, nullptr, nullptr, |
| "L"}, // Linux, ATI |
| |
| {nullptr, nullptr, "0x9583", nullptr, nullptr, |
| nullptr, "M"}, // ATI Radeon HD2600 on Mac |
| {nullptr, nullptr, "0x94c8", nullptr, nullptr, |
| nullptr, "M"}, // ATI Radeon HD2400 on Mac |
| |
| {"NVIDIA (0x10de)", nullptr, "0x0324", nullptr, nullptr, |
| nullptr, "M"}, // NVIDIA GeForce FX Go5200 (Mac) |
| {"10DE", "NVIDIA GeForce FX Go5200", nullptr, nullptr, nullptr, |
| nullptr, "W"}, // NVIDIA GeForce FX Go5200 (Win) |
| {"10de", nullptr, "0324", nullptr, nullptr, |
| nullptr, "L"}, // NVIDIA GeForce FX Go5200 (Linux) |
| |
| {"10de", nullptr, "029e", nullptr, nullptr, |
| nullptr, "L"}, // NVIDIA Quadro FX 1500 (Linux) |
| |
| // Various Quadro FX cards on Linux |
| {"10de", nullptr, "00cd", nullptr, "195.36.24", |
| nullptr, "L"}, |
| {"10de", nullptr, "00ce", nullptr, "195.36.24", |
| nullptr, "L"}, |
| // Driver version 260.19.6 on Linux |
| {"10de", nullptr, nullptr, nullptr, "260.19.6", |
| nullptr, "L"}, |
| |
| {"NVIDIA (0x10de)", nullptr, "0x0393", nullptr, nullptr, |
| nullptr, "M"}, // NVIDIA GeForce 7300 GT (Mac) |
| |
| // GPUs with < OpenGL 2.1 support |
| |
| // Intel Auburn |
| {"8086", nullptr, "7800", nullptr, nullptr, nullptr, nullptr}, |
| |
| // Intel Portola |
| {"8086", nullptr, "1240", nullptr, nullptr, nullptr, nullptr}, |
| |
| // Intel Whitney |
| {"8086", nullptr, "7121", nullptr, nullptr, nullptr, nullptr}, |
| {"8086", nullptr, "7123", nullptr, nullptr, nullptr, nullptr}, |
| {"8086", nullptr, "7125", nullptr, nullptr, nullptr, nullptr}, |
| |
| // Intel Solano |
| {"8086", nullptr, "1132", nullptr, nullptr, nullptr, nullptr}, |
| |
| // Intel Brookdale |
| {"8086", nullptr, "2562", nullptr, nullptr, nullptr, nullptr}, |
| |
| // Intel Almador |
| {"8086", nullptr, "3577", nullptr, nullptr, nullptr, nullptr}, |
| |
| // Intel Springdale |
| {"8086", nullptr, "2572", nullptr, nullptr, nullptr, nullptr}, |
| |
| // Intel Montara |
| {"8086", nullptr, "3582", nullptr, nullptr, nullptr, nullptr}, |
| {"8086", nullptr, "358e", nullptr, nullptr, nullptr, nullptr}, |
| |
| // Intel Grantsdale (2.1 desktop / ES 2.0 on Linux supported) |
| {"8086", nullptr, "2582", nullptr, nullptr, nullptr, "W"}, |
| {"8086", nullptr, "258a", nullptr, nullptr, nullptr, "W"}, |
| |
| // Intel Alviso |
| {"8086", nullptr, "2592", nullptr, nullptr, nullptr, "W"}, |
| |
| // Intel Lakeport |
| {"8086", nullptr, "2772", nullptr, nullptr, nullptr, "W"}, |
| |
| // Intel Calistoga |
| {"8086", nullptr, "27a2", nullptr, nullptr, nullptr, "W"}, |
| {"8086", nullptr, "27ae", nullptr, nullptr, nullptr, "W"}, |
| |
| // Intel Bearlake |
| {"8086", nullptr, "29c2", nullptr, nullptr, nullptr, "W"}, |
| {"8086", nullptr, "29b2", nullptr, nullptr, nullptr, "W"}, |
| {"8086", nullptr, "29d2", nullptr, nullptr, nullptr, "W"}, |
| |
| // Intel Pineview |
| {"8086", nullptr, "a001", nullptr, nullptr, nullptr, "W"}, |
| {"8086", nullptr, "a011", nullptr, nullptr, nullptr, "W"}, |
| |
| // Intel Lakeport |
| {"8086", nullptr, "2972", nullptr, nullptr, nullptr, "W"}, |
| |
| // Intel Broadwater |
| {"8086", nullptr, "2992", nullptr, nullptr, nullptr, "W"}, |
| {"8086", nullptr, "29a2", nullptr, nullptr, nullptr, "W"}, |
| |
| // Intel Bearlake 2 |
| {"8086", nullptr, "2982", nullptr, nullptr, nullptr, "W"}, |
| |
| // Intel Crestine |
| {"8086", nullptr, "2a02", nullptr, nullptr, nullptr, "W"}, |
| {"8086", nullptr, "2a12", nullptr, nullptr, nullptr, "W"}, |
| |
| // Display-only devices |
| |
| // Microsoft Basic Render Driver |
| {"1414", nullptr, "008c", nullptr, nullptr, nullptr, nullptr}, |
| |
| // Matrix MGA G200eW |
| {"102b", nullptr, "0532", nullptr, nullptr, nullptr, nullptr}, |
| |
| // VMWare SVGA 3D |
| {"15ad", nullptr, "0405", nullptr, nullptr, nullptr, nullptr}, |
| |
| // Citrix Display Only Adapter |
| {"5853", nullptr, "1002", nullptr, nullptr, nullptr, nullptr}, |
| |
| // SeaBIOS Display-Only Device? |
| {"1b36", nullptr, "0100", nullptr, nullptr, nullptr, nullptr}, |
| |
| // Devices incapable of reliably calling wglMakeCurrent |
| // when switching HDC |
| |
| // Intel Eaglelake |
| {"8086", nullptr, "2e42", nullptr, nullptr, nullptr, "W"}, |
| {"8086", nullptr, "2e92", nullptr, nullptr, nullptr, "W"}, |
| {"8086", nullptr, "2e12", nullptr, nullptr, nullptr, "W"}, |
| {"8086", nullptr, "2e32", nullptr, nullptr, nullptr, "W"}, |
| {"8086", nullptr, "2e22", nullptr, nullptr, nullptr, "W"}, |
| |
| // Intel Cantiga |
| {"8086", nullptr, "2a42", nullptr, nullptr, nullptr, "W"}, |
| |
| // Intel Ironlake |
| {"8086", nullptr, "0042", nullptr, nullptr, nullptr, "W"}, |
| {"8086", nullptr, "0046", nullptr, nullptr, nullptr, "W"}, |
| }; |
| |
| // If any blacklist entry matches any gpu, return true. |
| bool gpuinfo_query_blacklist(GpuInfoList* gpulist, |
| const BlacklistEntry* list, |
| int size) { |
| return gpuinfo_query_list(gpulist, list, size); |
| } |
| |
| #ifdef _WIN32 |
| static const WhitelistEntry sAngleWhitelist[] = { |
| // Make | Model | DeviceID | RevisionID | DriverVersion | Renderer | |
| // OS |
| // HD 3000 on Windows |
| {"8086", nullptr, "0116", nullptr, nullptr, |
| nullptr, "W"}, |
| {"8086", nullptr, "0126", nullptr, nullptr, |
| nullptr, "W"}, |
| {"8086", nullptr, "0102", nullptr, nullptr, |
| nullptr, "W"}, |
| }; |
| |
| static bool gpuinfo_query_whitelist(GpuInfoList *gpulist, |
| const WhitelistEntry *list, |
| int size) { |
| return gpuinfo_query_list(gpulist, list, size); |
| } |
| |
| #endif |
| |
| static const BlacklistEntry sSyncBlacklist[] = { |
| // Make | Model | DeviceID | RevisionID | DriverVersion | Renderer | |
| // OS |
| // All NVIDIA Quadro NVS and NVIDIA NVS GPUs on Windows |
| {"10de", nullptr, "06fd", nullptr, nullptr, nullptr, "W"}, // NVS 295 |
| {"10de", nullptr, "0a6a", nullptr, nullptr, nullptr, "W"}, // NVS 2100M |
| {"10de", nullptr, "0a6c", nullptr, nullptr, nullptr, "W"}, // NVS 5100M |
| {"10de", nullptr, "0ffd", nullptr, nullptr, nullptr, "W"}, // NVS 510 |
| {"10de", nullptr, "1056", nullptr, nullptr, nullptr, "W"}, // NVS 4200M |
| {"10de", nullptr, "10d8", nullptr, nullptr, nullptr, "W"}, // NVS 300 |
| {"10de", nullptr, "014a", nullptr, nullptr, nullptr, "W"}, // Quadro NVS 440 |
| {"10de", nullptr, "0165", nullptr, nullptr, nullptr, "W"}, // Quadro NVS 285 |
| {"10de", nullptr, "017a", nullptr, nullptr, nullptr, "W"}, // Quadro NVS (generic) |
| {"10de", nullptr, "018a", nullptr, nullptr, nullptr, "W"}, // Quadro NVS AGP8X (generic) |
| {"10de", nullptr, "018c", nullptr, nullptr, nullptr, "W"}, // Quadro NVS 50 PCI (generic) |
| {"10de", nullptr, "01db", nullptr, nullptr, nullptr, "W"}, // Quadro NVS 120M |
| {"10de", nullptr, "0245", nullptr, nullptr, nullptr, "W"}, // Quadro NVS 210S / NVIDIA GeForce 6150LE |
| {"10de", nullptr, "032a", nullptr, nullptr, nullptr, "W"}, // Quadro NVS 55/280 PCI |
| {"10de", nullptr, "040c", nullptr, nullptr, nullptr, "W"}, // Quadro NVS 570M / Mobile Quadro FX/NVS video card |
| {"10de", nullptr, "0429", nullptr, nullptr, nullptr, "W"}, // Quadro NVS 135M or Quadro NVS 140M |
| {"10de", nullptr, "042b", nullptr, nullptr, nullptr, "W"}, // Quadro NVS 135M |
| {"10de", nullptr, "042f", nullptr, nullptr, nullptr, "W"}, // Quadro NVS 290 |
| {"10de", nullptr, "06ea", nullptr, nullptr, nullptr, "W"}, // quadro nvs 150m |
| {"10de", nullptr, "06eb", nullptr, nullptr, nullptr, "W"}, // Quadro NVS 160M |
| {"10de", nullptr, "06f8", nullptr, nullptr, nullptr, "W"}, // Quadro NVS 420 |
| {"10de", nullptr, "06fa", nullptr, nullptr, nullptr, "W"}, // Quadro NVS 450 |
| {"10de", nullptr, "0a2c", nullptr, nullptr, nullptr, "W"}, // Quadro NVS 5100M |
| }; |
| |
| namespace { |
| |
| // A set of global variables for GPU information - a single instance of |
| // GpuInfoList and its loading thread. |
| class Globals { |
| DISALLOW_COPY_AND_ASSIGN(Globals); |
| |
| public: |
| Globals() |
| : mAsyncLoadThread([this]() { |
| getGpuInfoListNative(&mGpuInfoList); |
| |
| mGpuInfoList.blacklist_status = gpuinfo_query_blacklist( |
| &mGpuInfoList, sGpuBlacklist, arraySize(sGpuBlacklist)); |
| #ifdef _WIN32 |
| mGpuInfoList.Anglelist_status = |
| gpuinfo_query_whitelist(&mGpuInfoList, sAngleWhitelist, |
| arraySize(sAngleWhitelist)); |
| #else |
| mGpuInfoList.Anglelist_status = false; |
| #endif |
| mGpuInfoList.SyncBlacklist_status = gpuinfo_query_blacklist( |
| &mGpuInfoList, sSyncBlacklist, arraySize(sSyncBlacklist)); |
| |
| mGpuInfoList.VulkanBlacklist_status = |
| !isVulkanSafeToUseNative(); |
| |
| }) { |
| mAsyncLoadThread.start(); |
| } |
| |
| const GpuInfoList& gpuInfoList() { |
| mAsyncLoadThread.wait(); |
| return mGpuInfoList; |
| } |
| |
| private: |
| GpuInfoList mGpuInfoList; |
| |
| // Separate thread for GPU info querying: |
| // |
| // GPU information querying may take a while, but the implementation is |
| // expected to abort if it's over reasonable limits. That's why we use a |
| // separate thread that's started as soon as possible at startup and then |
| // wait for it synchronoulsy before using the information. |
| // |
| FunctorThread mAsyncLoadThread; |
| }; |
| |
| } // namespace |
| |
| static Globals* sGlobals() { |
| static Globals* g = new Globals; |
| return g; |
| } |
| |
| void async_query_host_gpu_start() { |
| sGlobals(); |
| } |
| |
| const GpuInfoList& globalGpuInfoList() { |
| return sGlobals()->gpuInfoList(); |
| } |
| |
| bool async_query_host_gpu_blacklisted() { |
| return globalGpuInfoList().blacklist_status; |
| } |
| |
| bool async_query_host_gpu_AngleWhitelisted() { |
| return globalGpuInfoList().Anglelist_status; |
| } |
| |
| bool async_query_host_gpu_SyncBlacklisted() { |
| return globalGpuInfoList().SyncBlacklist_status; |
| } |
| |
| bool async_query_host_gpu_VulkanBlacklisted() { |
| return globalGpuInfoList().VulkanBlacklist_status; |
| } |
| |
| void setGpuBlacklistStatus(bool switchedToSoftware) { |
| // We know what we're doing here: GPU list doesn't change after it's fully |
| // loaded. |
| const_cast<GpuInfoList&>(globalGpuInfoList()).blacklist_status = |
| switchedToSoftware; |
| } |