blob: a7f93bb242e6d70f2a5e7b960a13099bc1da4c68 [file] [log] [blame]
// Copyright 2015 The Android Open Source Project
//
// This software is licensed under the terms of the GNU General Public
// License version 2, as published by the Free Software Foundation, and
// may be copied, distributed, and modified under those terms.
//
// This program 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 General Public License for more details.
#include "android/opengl/emugl_config.h"
#include "android/opengl/gpuinfo.h"
#include "android/base/StringFormat.h"
#include "android/base/system/System.h"
#include "android/globals.h"
#include "android/opengl/EmuglBackendList.h"
#include <string>
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DEBUG 0
#if DEBUG
#define D(...) printf(__VA_ARGS__)
#else
#define D(...) ((void)0)
#endif
using android::base::RunOptions;
using android::base::StringFormat;
using android::base::System;
using android::opengl::EmuglBackendList;
static EmuglBackendList* sBackendList = NULL;
static void resetBackendList(int bitness) {
delete sBackendList;
sBackendList = new EmuglBackendList(
System::get()->getLauncherDirectory().c_str(), bitness);
}
static bool stringVectorContains(const std::vector<std::string>& list,
const char* value) {
for (size_t n = 0; n < list.size(); ++n) {
if (!strcmp(list[n].c_str(), value)) {
return true;
}
}
return false;
}
bool isHostGpuBlacklisted() {
return async_query_host_gpu_blacklisted();
}
void setGpuBlacklistStatus(bool switchedSoftware) {
GpuInfoList::get()->blacklist_status =
switchedSoftware;
}
// Get a description of host GPU properties.
// Need to free after use.
emugl_host_gpu_prop_list emuglConfig_get_host_gpu_props() {
GpuInfoList* gpulist = GpuInfoList::get();
emugl_host_gpu_prop_list res;
res.num_gpus = gpulist->infos.size();
res.props = new emugl_host_gpu_props[res.num_gpus];
const std::vector<GpuInfo>& infos = gpulist->infos;
for (int i = 0; i < res.num_gpus; i++) {
res.props[i].make = strdup(infos[i].make.c_str());
res.props[i].model = strdup(infos[i].model.c_str());
res.props[i].device_id = strdup(infos[i].device_id.c_str());
res.props[i].revision_id = strdup(infos[i].revision_id.c_str());
res.props[i].version = strdup(infos[i].version.c_str());
res.props[i].renderer = strdup(infos[i].renderer.c_str());
}
return res;
}
SelectedRenderer emuglConfig_get_renderer(const char* gpu_mode) {
if (!gpu_mode) {
return SELECTED_RENDERER_UNKNOWN;
} else if (!strcmp(gpu_mode, "host") ||
!strcmp(gpu_mode, "on")) {
return SELECTED_RENDERER_HOST;
} else if (!strcmp(gpu_mode, "off")) {
return SELECTED_RENDERER_OFF;
} else if (!strcmp(gpu_mode, "guest")) {
return SELECTED_RENDERER_GUEST;
} else if (!strcmp(gpu_mode, "mesa")) {
return SELECTED_RENDERER_MESA;
} else if (!strcmp(gpu_mode, "swiftshader")) {
return SELECTED_RENDERER_SWIFTSHADER;
} else if (!strcmp(gpu_mode, "angle")) {
return SELECTED_RENDERER_ANGLE;
} else if (!strcmp(gpu_mode, "error")) {
return SELECTED_RENDERER_ERROR;
} else {
return SELECTED_RENDERER_UNKNOWN;
}
}
void free_emugl_host_gpu_props(emugl_host_gpu_prop_list proplist) {
for (int i = 0; i < proplist.num_gpus; i++) {
free(proplist.props[i].make);
free(proplist.props[i].model);
free(proplist.props[i].device_id);
free(proplist.props[i].revision_id);
free(proplist.props[i].version);
free(proplist.props[i].renderer);
}
delete [] proplist.props;
}
bool emuglConfig_init(EmuglConfig* config,
bool gpu_enabled,
const char* gpu_mode,
const char* gpu_option,
int bitness,
bool no_window,
bool blacklisted,
bool has_guest_renderer) {
D("%s: blacklisted=%d has_guest_renderer=%d\n",
__FUNCTION__,
blacklisted,
has_guest_renderer);
// zero all fields first.
memset(config, 0, sizeof(*config));
// The value of '-gpu <mode>' overrides the hardware properties,
// except if <mode> is 'auto'.
if (gpu_option) {
if (!strcmp(gpu_option, "on") || !strcmp(gpu_option, "enable")) {
gpu_enabled = true;
if (!gpu_mode || !strcmp(gpu_mode, "auto")) {
gpu_mode = "host";
}
} else if (!strcmp(gpu_option, "off") ||
!strcmp(gpu_option, "disable") ||
!strcmp(gpu_option, "guest")) {
gpu_mode = gpu_option;
gpu_enabled = false;
} else if (!strcmp(gpu_option, "auto")) {
// Nothing to do, use gpu_mode set from
// hardware properties instead.
} else {
gpu_enabled = true;
gpu_mode = gpu_option;
}
} else {
// Support "hw.gpu.mode=on" in config.ini
if (gpu_mode && (!strcmp(gpu_mode, "on") || !strcmp(gpu_mode, "enable"))) {
gpu_enabled = true;
gpu_mode = "host";
}
}
if (gpu_mode &&
(!strcmp(gpu_mode, "guest") ||
!strcmp(gpu_mode, "off"))) {
gpu_enabled = false;
}
if (!gpu_enabled) {
config->enabled = false;
snprintf(config->backend, sizeof(config->backend), "%s", gpu_mode);
snprintf(config->status, sizeof(config->status),
"GPU emulation is disabled");
return true;
}
if (!bitness) {
bitness = System::get()->getProgramBitness();
}
config->bitness = bitness;
resetBackendList(bitness);
// Check that the GPU mode is a valid value. 'auto' means determine
// the best mode depending on the environment. Its purpose is to
// enable 'swiftshader' mode automatically when NX or Chrome Remote Desktop
// is detected.
if (!strcmp(gpu_mode, "auto")) {
// The default will be 'host' unless:
// 1. NX or Chrome Remote Desktop is detected, or |no_window| is true.
// 2. The user's host GPU is on the blacklist.
std::string sessionType;
if (System::get()->isRemoteSession(&sessionType)) {
D("%s: %s session detected\n", __FUNCTION__, sessionType.c_str());
if (!sBackendList->contains("swiftshader")) {
config->enabled = false;
gpu_mode = "off";
snprintf(config->backend, sizeof(config->backend), "%s", gpu_mode);
snprintf(config->status, sizeof(config->status),
"GPU emulation is disabled under %s without Swiftshader",
sessionType.c_str());
return true;
}
D("%s: 'swiftshader' mode auto-selected\n", __FUNCTION__);
gpu_mode = "swiftshader";
} else if (no_window || blacklisted) {
if (stringVectorContains(sBackendList->names(), "swiftshader")) {
D("%s: Headless (-no-window) mode (or blacklisted GPU driver)"
", using Swiftshader backend\n",
__FUNCTION__);
gpu_mode = "swiftshader";
} else if (!has_guest_renderer) {
D("%s: Headless (-no-window) mode (or blacklisted GPU driver)"
" without Swiftshader, forcing '-gpu off'\n",
__FUNCTION__);
config->enabled = false;
gpu_mode = "off";
snprintf(config->backend, sizeof(config->backend), "%s", gpu_mode);
snprintf(config->status, sizeof(config->status),
"GPU emulation is disabled (-no-window without Swiftshader)");
return true;
} else {
D("%s: Headless (-no-window) mode (or blacklisted GPU driver)"
", using guest GPU backend\n",
__FUNCTION__);
config->enabled = false;
gpu_mode = "off";
snprintf(config->backend, sizeof(config->backend), "%s", gpu_mode);
snprintf(config->status, sizeof(config->status),
"GPU emulation is in the guest");
gpu_mode = "guest";
return true;
}
} else {
D("%s: 'host' mode auto-selected\n", __FUNCTION__);
gpu_mode = "host";
}
}
// 'host' is a special value corresponding to the default translation
// to desktop GL, 'guest' does not use host-side emulation,
// anything else must be checked against existing host-side backends.
if (strcmp(gpu_mode, "host") != 0 && strcmp(gpu_mode, "guest") != 0) {
const std::vector<std::string>& backends = sBackendList->names();
if (!stringVectorContains(backends, gpu_mode)) {
std::string error = StringFormat(
"Invalid GPU mode '%s', use one of: on off host guest", gpu_mode);
for (size_t n = 0; n < backends.size(); ++n) {
error += " ";
error += backends[n];
}
config->enabled = false;
gpu_mode = "error";
snprintf(config->backend, sizeof(config->backend), "%s", gpu_mode);
snprintf(config->status, sizeof(config->status), "%s",
error.c_str());
return false;
}
}
if (strcmp(gpu_mode, "guest")) {
config->enabled = true;
}
snprintf(config->backend, sizeof(config->backend), "%s", gpu_mode);
snprintf(config->status, sizeof(config->status),
"GPU emulation enabled using '%s' mode", gpu_mode);
return true;
}
void emuglConfig_setupEnv(const EmuglConfig* config) {
System* system = System::get();
if (!config->enabled) {
// There is no real GPU emulation. As a special case, define
// SDL_RENDER_DRIVER to 'software' to ensure that the
// software SDL renderer is being used. This allows one
// to run with '-gpu off' under NX and Chrome Remote Desktop
// properly.
system->envSet("SDL_RENDER_DRIVER", "software");
return;
}
// $EXEC_DIR/<lib>/ is already added to the library search path by default,
// since generic libraries are bundled there. We may need more though:
resetBackendList(config->bitness);
if (strcmp(config->backend, "host") != 0) {
// If the backend is not 'host', we also need to add the
// backend directory.
std::string dir = sBackendList->getLibDirPath(config->backend);
if (dir.size()) {
D("Adding to the library search path: %s\n", dir.c_str());
system->addLibrarySearchDir(dir);
}
}
if (!strcmp(config->backend, "host")) {
// Nothing more to do for the 'host' backend.
return;
}
// For now, EmuGL selects its own translation libraries for
// EGL/GLES libraries, unless the following environment
// variables are defined:
// ANDROID_EGL_LIB
// ANDROID_GLESv1_LIB
// ANDROID_GLESv2_LIB
//
// If a backend provides one of these libraries, use it.
std::string lib;
if (sBackendList->getBackendLibPath(
config->backend, EmuglBackendList::LIBRARY_EGL, &lib)) {
system->envSet("ANDROID_EGL_LIB", lib);
}
if (sBackendList->getBackendLibPath(
config->backend, EmuglBackendList::LIBRARY_GLESv1, &lib)) {
system->envSet("ANDROID_GLESv1_LIB", lib);
} else if (strcmp(config->backend, "mesa")) {
fprintf(stderr, "OpenGL backend '%s' without OpenGL ES 1.x library detected. "
"Using GLESv2 only.\n",
config->backend);
// A GLESv1 lib is optional---we can deal with a GLESv2 only
// backend by using a GLESv1->GLESv2 emulation library.
system->envSet("ANDROID_GLESv1_LIB", sBackendList->getGLES12TranslatorLibName().c_str());
}
if (sBackendList->getBackendLibPath(
config->backend, EmuglBackendList::LIBRARY_GLESv2, &lib)) {
system->envSet("ANDROID_GLESv2_LIB", lib);
}
if (!strcmp(config->backend, "mesa")) {
fprintf(stderr, "WARNING: The Mesa software renderer is deprecated. "
"Use Swiftshader (-gpu swiftshader) for software rendering.\n");
system->envSet("ANDROID_GL_LIB", "mesa");
system->envSet("ANDROID_GL_SOFTWARE_RENDERER", "1");
}
}