| // 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/emugl_config.h" |
| |
| #include "aemu/base/StringFormat.h" |
| #include "aemu/base/system/System.h" |
| #include "host-common/globals.h" |
| #include "host-common/opengl/EmuglBackendList.h" |
| #include "host-common/opengl/gpuinfo.h" |
| #include "host-common/opengl/misc.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(...) crashhandler_append_message_format(__VA_ARGS__) |
| #define D(...) |
| #endif |
| |
| using android::base::StringFormat; |
| using android::opengl::EmuglBackendList; |
| |
| static EmuglBackendList* sBackendList = NULL; |
| |
| static void resetBackendList(int bitness) { |
| delete sBackendList; |
| std::vector<std::string> fixedBackendNames = { |
| "swiftshader_indirect", |
| "angle_indirect", |
| }; |
| sBackendList = new EmuglBackendList(64, fixedBackendNames); |
| } |
| |
| 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(); |
| } |
| |
| // Get a description of host GPU properties. |
| // Need to free after use. |
| emugl_host_gpu_prop_list emuglConfig_get_host_gpu_props() { |
| const GpuInfoList& gpulist = globalGpuInfoList(); |
| 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, "angle9")) { |
| return SELECTED_RENDERER_ANGLE9; |
| } else if (!strcmp(gpu_mode, "swiftshader_indirect")) { |
| return SELECTED_RENDERER_SWIFTSHADER_INDIRECT; |
| } else if (!strcmp(gpu_mode, "angle_indirect")) { |
| return SELECTED_RENDERER_ANGLE_INDIRECT; |
| } else if (!strcmp(gpu_mode, "angle9_indirect")) { |
| return SELECTED_RENDERER_ANGLE9_INDIRECT; |
| } else if (!strcmp(gpu_mode, "error")) { |
| return SELECTED_RENDERER_ERROR; |
| } else { |
| return SELECTED_RENDERER_UNKNOWN; |
| } |
| } |
| |
| static SelectedRenderer sCurrentRenderer = |
| SELECTED_RENDERER_UNKNOWN; |
| |
| SelectedRenderer emuglConfig_get_current_renderer() { |
| return sCurrentRenderer; |
| } |
| |
| static std::string sGpuOption; |
| |
| const char* emuglConfig_get_user_gpu_option() { |
| return sGpuOption.c_str(); |
| } |
| |
| const char* emuglConfig_renderer_to_string(SelectedRenderer renderer) { |
| switch (renderer) { |
| case SELECTED_RENDERER_UNKNOWN: |
| return "(Unknown)"; |
| case SELECTED_RENDERER_HOST: |
| return "Host"; |
| case SELECTED_RENDERER_OFF: |
| return "Off"; |
| case SELECTED_RENDERER_GUEST: |
| return "Guest"; |
| case SELECTED_RENDERER_MESA: |
| return "Mesa"; |
| case SELECTED_RENDERER_SWIFTSHADER: |
| return "Swiftshader"; |
| case SELECTED_RENDERER_ANGLE: |
| return "Angle"; |
| case SELECTED_RENDERER_ANGLE9: |
| return "Angle9"; |
| case SELECTED_RENDERER_SWIFTSHADER_INDIRECT: |
| return "Swiftshader Indirect"; |
| case SELECTED_RENDERER_ANGLE_INDIRECT: |
| return "Angle Indirect"; |
| case SELECTED_RENDERER_ANGLE9_INDIRECT: |
| return "Angle9 Indirect"; |
| case SELECTED_RENDERER_ERROR: |
| return "(Error)"; |
| } |
| return "(Bad value)"; |
| } |
| |
| bool emuglConfig_current_renderer_supports_snapshot() { |
| if (aemu_get_android_hw()->hw_arc) { |
| return sCurrentRenderer == SELECTED_RENDERER_OFF || |
| sCurrentRenderer == SELECTED_RENDERER_GUEST; |
| } |
| return sCurrentRenderer == SELECTED_RENDERER_HOST || |
| sCurrentRenderer == SELECTED_RENDERER_OFF || |
| sCurrentRenderer == SELECTED_RENDERER_GUEST || |
| sCurrentRenderer == SELECTED_RENDERER_ANGLE_INDIRECT || |
| sCurrentRenderer == SELECTED_RENDERER_SWIFTSHADER_INDIRECT; |
| } |
| |
| 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; |
| } |
| |
| static void setCurrentRenderer(const char* gpuMode) { |
| sCurrentRenderer = emuglConfig_get_renderer(gpuMode); |
| } |
| |
| 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, |
| int uiPreferredBackend, |
| bool use_host_vulkan) { |
| D("%s: blacklisted=%d has_guest_renderer=%d, mode: %s, option: %s\n", |
| __FUNCTION__, |
| blacklisted, |
| has_guest_renderer, |
| gpu_mode, gpu_option); |
| |
| // zero all fields first. |
| memset(config, 0, sizeof(*config)); |
| |
| bool host_set_in_hwconfig = false; |
| bool has_auto_no_window = false; |
| |
| bool hasUiPreference = uiPreferredBackend != WINSYS_GLESBACKEND_PREFERENCE_AUTO; |
| |
| // The value of '-gpu <mode>' overrides both the hardware properties |
| // and the UI setting, except if <mode> is 'auto'. |
| if (gpu_option) { |
| sGpuOption = 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 if (!strcmp(gpu_option, "auto-no-window")) { |
| // Nothing to do, use gpu_mode set from |
| // hardware properties instead. |
| has_auto_no_window = true; |
| } else { |
| gpu_enabled = true; |
| gpu_mode = gpu_option; |
| } |
| } else { |
| // Support "hw.gpu.mode=on" in config.ini |
| if (gpu_enabled && gpu_mode && ( |
| !strcmp(gpu_mode, "on") || |
| !strcmp(gpu_mode, "enable") || |
| !strcmp(gpu_mode, "host"))) { |
| gpu_enabled = true; |
| gpu_mode = "host"; |
| host_set_in_hwconfig = true; |
| } |
| } |
| sGpuOption = gpu_mode; |
| |
| if (gpu_mode && |
| (!strcmp(gpu_mode, "guest") || |
| !strcmp(gpu_mode, "off"))) { |
| gpu_enabled = false; |
| } |
| |
| if (!gpu_option && hasUiPreference) { |
| gpu_enabled = true; |
| gpu_mode = "auto"; |
| } |
| |
| 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"); |
| setCurrentRenderer(gpu_mode); |
| return true; |
| } |
| |
| if (gpu_mode && !strcmp("angle", gpu_mode)) { |
| gpu_mode = "angle_indirect"; |
| } |
| |
| if (gpu_mode && !strcmp("swiftshader", gpu_mode)) { |
| gpu_mode = "swiftshader_indirect"; |
| } |
| |
| if (!bitness) { |
| bitness = 64; |
| } |
| |
| config->bitness = bitness; |
| config->use_host_vulkan = use_host_vulkan; |
| 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 ((gpu_mode && !strcmp(gpu_mode, "auto")) || host_set_in_hwconfig) { |
| // 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 (!has_auto_no_window && (no_window || (blacklisted && !hasUiPreference))) { |
| if (stringVectorContains(sBackendList->names(), "swiftshader")) { |
| D("%s: Headless mode or blacklisted GPU driver, " |
| "using Swiftshader backend\n", |
| __FUNCTION__); |
| gpu_mode = "swiftshader_indirect"; |
| } 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)"); |
| setCurrentRenderer(gpu_mode); |
| 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"; |
| setCurrentRenderer(gpu_mode); |
| return true; |
| } |
| } else { |
| switch (uiPreferredBackend) { |
| case WINSYS_GLESBACKEND_PREFERENCE_ANGLE: |
| gpu_mode = "angle_indirect"; |
| break; |
| case WINSYS_GLESBACKEND_PREFERENCE_ANGLE9: |
| gpu_mode = "angle_indirect"; |
| break; |
| case WINSYS_GLESBACKEND_PREFERENCE_SWIFTSHADER: |
| gpu_mode = "swiftshader_indirect"; |
| break; |
| case WINSYS_GLESBACKEND_PREFERENCE_NATIVEGL: |
| gpu_mode = "host"; |
| break; |
| default: |
| gpu_mode = "host"; |
| break; |
| } |
| D("%s: auto-selected %s based on conditions and UI preference %d\n", |
| __func__, gpu_mode, uiPreferredBackend); |
| } |
| } |
| |
| // '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 (!gpu_mode || |
| (strcmp(gpu_mode, "host") != 0 && strcmp(gpu_mode, "guest") != 0)) { |
| const std::vector<std::string>& backends = sBackendList->names(); |
| if (!gpu_mode || !stringVectorContains(backends, gpu_mode)) { |
| std::string error = StringFormat( |
| "Invalid GPU mode '%s', use one of: host swiftshader_indirect. " |
| "If you're already using one of those modes, " |
| "the emulator installation may be corrupt. " |
| "Please re-install the emulator.", gpu_mode); |
| |
| for (size_t n = 0; n < backends.size(); ++n) { |
| error += " "; |
| error += backends[n]; |
| } |
| |
| D("%s: Error: [%s]\n", __func__, error.c_str()); |
| fprintf(stderr, "%s: %s\n", __func__, error.c_str()); |
| |
| 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()); |
| setCurrentRenderer(gpu_mode); |
| 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); |
| setCurrentRenderer(gpu_mode); |
| D("%s: %s\n", __func__, config->status); |
| return true; |
| } |
| |
| void emuglConfig_setupEnv(const EmuglConfig* config) { |
| if (config->use_host_vulkan) { |
| android::base::setEnvironmentVariable("ANDROID_EMU_VK_ICD", ""); |
| } else if (sCurrentRenderer == SELECTED_RENDERER_SWIFTSHADER_INDIRECT) { |
| // Use Swiftshader vk icd if using swiftshader_indirect |
| android::base::setEnvironmentVariable("ANDROID_EMU_VK_ICD", "swiftshader"); |
| } |
| |
| 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. |
| android::base::setEnvironmentVariable("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()); |
| // fprintf(stderr, "%s: non-host backends not supported\n", __func__); |
| // abort(); |
| // android::base::addLibrarySearchDir(dir); |
| } |
| } |
| |
| if (!strcmp(config->backend, "host")) { |
| // Nothing more to do for the 'host' backend. |
| return; |
| } |
| |
| if (!strcmp(config->backend, "angle_indirect") |
| || !strcmp(config->backend, "swiftshader_indirect")) { |
| android::base::setEnvironmentVariable("ANDROID_EGL_ON_EGL", "1"); |
| 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)) { |
| android::base::setEnvironmentVariable("ANDROID_EGL_LIB", lib); |
| } |
| if (sBackendList->getBackendLibPath( |
| config->backend, EmuglBackendList::LIBRARY_GLESv1, &lib)) { |
| android::base::setEnvironmentVariable("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 CoreProfileEngine in the Translator. |
| } |
| |
| if (sBackendList->getBackendLibPath( |
| config->backend, EmuglBackendList::LIBRARY_GLESv2, &lib)) { |
| android::base::setEnvironmentVariable("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"); |
| android::base::setEnvironmentVariable("ANDROID_GL_LIB", "mesa"); |
| android::base::setEnvironmentVariable("ANDROID_GL_SOFTWARE_RENDERER", "1"); |
| } |
| } |