blob: 4231f535075d7e8e64cc569a9a45d63331f8029e [file] [log] [blame]
// 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;
}