blob: 6e12303a5cb7b611a84cf819085bdd8088a472ad [file] [log] [blame]
/*
* ARM Android emulator OpenGLES backend
*
* Copyright (c) 2014 Android Open Source Project
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2 or later, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* Handle connections to the 'opengles' pipe from Android guests and route
* traffic over this pipe to the GPU emulation libraries.
*/
#include "hw/misc/android_opengles.h"
#include "hw/misc/android_pipe.h"
#include "qemu/compiler.h"
#include "qemu-common.h"
#include <limits.h>
#ifdef _WIN32
#include <errno.h>
#include <windows.h>
#else
#include <dlfcn.h>
#endif
// #define DEBUG_OPENGLES
#ifdef DEBUG_OPENGLES
#define DPRINTF(fmt, ...) \
do { fprintf(stderr, "adb_debug: " fmt , ## __VA_ARGS__); } while (0)
#else
#define DPRINTF(fmt, ...) do {} while(0)
#endif
// Loading shared libraries and probing their symbols.
typedef struct DynamicLibrary DynamicLibrary;
static DynamicLibrary* dynamic_library_open(const char* library_name)
{
char path[PATH_MAX];
#ifdef _WIN32
static const char kDllExtension[] = ".dll";
#elif defined(__APPLE__)
static const char kDllExtension[] = ".dylib";
#else
static const char kDllExtension[] = ".so";
#endif
if (!strchr(library_name, '.')) {
snprintf(path, sizeof path, "%s%s", library_name, kDllExtension);
} else {
snprintf(path, sizeof path, "%s", library_name);
}
#ifdef _WIN32
return (DynamicLibrary*)LoadLibrary(path);
#else
return (DynamicLibrary*)dlopen(path, RTLD_NOW);
#endif
}
static void dynamic_library_close(DynamicLibrary* library)
{
#ifdef _WIN32
if (library) {
FreeLibrary((HMODULE)library);
}
#else
if (library) {
dlclose(library);
}
#endif
}
static void* dynamic_library_probe(DynamicLibrary* library,
const char* symbol)
{
if (!library || !symbol || !symbol[0]) {
return NULL;
}
#ifdef _WIN32
return GetProcAddress((HMODULE)library, symbol);
#else
return dlsym(library, symbol);
#endif
}
//
// Loading the GPU shared libraries
//
// Name of the GLES rendering library we're going to use.
#if UINTPTR_MAX == UINT32_MAX
#define RENDERER_LIB_NAME "libOpenglRender"
#elif UINTPTR_MAX == UINT64_MAX
#define RENDERER_LIB_NAME "lib64OpenglRender"
#else
#error Unknown UINTPTR_MAX
#endif
// NOTE: The declarations below should be equivalent to those in
// <libOpenglRender/render_api_platform_types.h>
#ifdef _WIN32
typedef HDC FBNativeDisplayType;
typedef HWND FBNativeWindowType;
#elif defined(__linux__)
// Really a Window, which is defined as 32-bit unsigned long on all platforms
// but we don't want to include the X11 headers here.
typedef uint32_t FBNativeWindowType;
#elif defined(__APPLE__)
typedef void* FBNativeWindowType;
#else
#warning "unsupported platform"
#endif
// List of GPU emulation library functions.
#define RENDERER_FUNCTIONS_LIST \
FUNCTION_(int, initLibrary, (void)) \
FUNCTION_(int, setStreamMode, (int mode)) \
FUNCTION_(int, initOpenGLRenderer, (int width, int height, char* addr, size_t addrLen)) \
FUNCTION_(void, getHardwareStrings, (const char** vendors, const char** renderer, const char** version)) \
FUNCTION_(void, setPostCallback, (AndroidGLESOnPostFunc onPost, void* onPostContext)) \
FUNCTION_(int, createOpenGLSubwindow, (FBNativeWindowType window, int x, int y, int width, int height, float zRot)) \
FUNCTION_(int, destroyOpenGLSubwindow, (void)) \
FUNCTION_(void, setOpenGLDisplayRotation, (float zRot)) \
FUNCTION_(void, repaintOpenGLDisplay, (void)) \
FUNCTION_(int, stopOpenGLRenderer, (void)) \
// Name of static variable that points to symbol |name| in the GPU
// emulation library.
#define EMUGL_WRAPPER(name) emugl_ ## name
// Define the corresponding function pointers as global variables, with
// an 'emugl_' prefix.
#define FUNCTION_(ret, name, sig) \
static ret (*EMUGL_WRAPPER(name)) sig = NULL;
RENDERER_FUNCTIONS_LIST
#undef FUNCTION_
// Define a function that initializes the function pointers by looking up
// the symbols from the shared library.
static int init_opengles_emulation_functions(DynamicLibrary* library)
{
void *symbol;
#define FUNCTION_(ret, name, sig) \
symbol = dynamic_library_probe(library, #name); \
if (!symbol) { \
DPRINTF("GLES emulation: Could not find required symbol (%s): %s\n", #name); \
return -1; \
} \
EMUGL_WRAPPER(name) = symbol;
RENDERER_FUNCTIONS_LIST
#undef FUNCTION_
return 0;
}
// list of constants to be passed to setStreamMode, which determines how
// to send/receive wire protocol data to/from the library.
// DEFAULT -> try to use the best for the current platform.
// TCP -> use a TCP socket to send the protocol bytes to the library.
// UNIX -> use a Unix domain socket (faster than TCP, but Unix-only).
// WIN32_PIPE -> use a Win32 PIPE (unsupported by the library for now!).
enum {
STREAM_MODE_DEFAULT = 0,
STREAM_MODE_TCP = 1,
STREAM_MODE_UNIX = 2,
STREAM_MODE_WIN32_PIPE = 3,
};
struct AndroidGLES {
bool init;
DynamicLibrary* renderer_lib;
bool renderer_started;
char renderer_address[256];
};
static AndroidGLES sState = {
.init = false,
.renderer_lib = NULL,
.renderer_started = false,
};
AndroidGLES* android_gles_init(void)
{
AndroidGLES* state = &sState;
if (state->init) {
return state;
}
// Try to load the library.
state->renderer_lib = dynamic_library_open(RENDERER_LIB_NAME);
if (!state->renderer_lib) {
DPRINTF("Could not load GPU emulation library!\n");
return NULL;
}
// Resolve all required symbols from it.
if (init_opengles_emulation_functions(state->renderer_lib) < 0) {
DPRINTF("OpenGLES emulation library mismatch. Bes ure to use the correct version!\n");
goto BAD_EXIT;
}
// Call its initialization function.
if (!EMUGL_WRAPPER(initLibrary)()) {
DPRINTF("OpenGLES initialization failed!\n");
goto BAD_EXIT;
}
#ifdef _WIN32
// NOTE: Win32 PIPE support is still not implemented.
EMUGL_WRAPPER(setStreamMode)(STREAM_MODE_TCP);
#else
EMUGL_WRAPPER(setStreamMode)(STREAM_MODE_UNIX);
#endif
state->init = true;
return state;
BAD_EXIT:
DPRINTF("OpenGLES library could not be initialized\n");
dynamic_library_close(state->renderer_lib);
state->renderer_lib = NULL;
return NULL;
}
static void extract_base_string(char* dst, const char* src, size_t dstSize)
{
const char* begin = strchr(src, '(');
const char* end = strrchr(src, ')');
if (!begin || !end) {
pstrcpy(dst, dstSize, src);
return;
}
begin += 1;
// "foo (bar)"
// ^ ^
// b e
// = 5 8
// substring with NUL-terminator is end-begin+1 bytes
if (end - begin + 1 > dstSize) {
end = begin + dstSize - 1;
}
pstrcpy(dst, end - begin + 1, begin);
}
void android_gles_get_hardware_strings(char *vendor,
size_t vendor_buffer_size,
char *renderer,
size_t renderer_buffer_size,
char *version,
size_t version_buffer_size)
{
const char *vendor_src, *renderer_src, *version_src;
AndroidGLES *state = &sState;
if (!state->renderer_started) {
DPRINTF("Can't get OpenGL ES hardware strings when renderer not started");
vendor[0] = renderer[0] = version[0] = '\0';
return;
}
EMUGL_WRAPPER(getHardwareStrings)(&vendor_src,
&renderer_src,
&version_src);
if (!vendor_src) vendor_src = "";
if (!renderer_src) renderer_src = "";
if (!version_src) version_src = "";
/* Special case for the default ES to GL translators: extract the strings
* of the underlying OpenGL implementation. */
if (strncmp(vendor_src, "Google", 6) == 0 &&
strncmp(renderer_src, "Android Emulator OpenGL ES Translator", 37) == 0) {
extract_base_string(vendor, vendor_src, vendor_buffer_size);
extract_base_string(renderer, renderer_src, renderer_buffer_size);
extract_base_string(version, version_src, version_buffer_size);
} else {
pstrcpy(vendor, vendor_buffer_size, vendor_src);
pstrcpy(renderer, renderer_buffer_size, renderer_src);
pstrcpy(version, version_buffer_size, version_src);
}
}
int android_gles_start(AndroidGLES* state, int width, int height)
{
if (!state->renderer_lib) {
DPRINTF("Can't start OpenGLES renderer without support libraries\n");
return -1;
}
if (state->renderer_started) {
// Already started.
return 0;
}
if (!EMUGL_WRAPPER(initOpenGLRenderer)(
width,
height,
state->renderer_address,
sizeof(state->renderer_address))) {
DPRINTF("Can't start OpenGLES renderer !?\n");
return -1;
}
state->renderer_started = true;
return 0;
}
void android_gles_set_post_callback(AndroidGLES* state,
AndroidGLESOnPostFunc on_post,
void* on_post_context)
{
if (state->renderer_lib) {
EMUGL_WRAPPER(setPostCallback)(on_post, on_post_context);
}
}
int android_gles_show_window(AndroidGLES* state,
void* window,
int x,
int y,
int width,
int height,
float rotation)
{
if (state->renderer_started) {
int success = EMUGL_WRAPPER(createOpenGLSubwindow)(
(FBNativeWindowType)(uintptr_t)window,
x,
y,
width,
height,
rotation);
return success ? 0 : -1;
} else {
return -1;
}
}
void android_gles_hide_window(AndroidGLES* state)
{
if (state->renderer_started) {
EMUGL_WRAPPER(destroyOpenGLSubwindow)();
}
}
void android_gles_redraw_window(AndroidGLES* state) {
if (state->renderer_started) {
EMUGL_WRAPPER(repaintOpenGLDisplay)();
}
}
void android_gles_server_path(char* buff, size_t buff_size)
{
AndroidGLES *state = &sState;
pstrcpy(buff, buff_size, state->renderer_address);
}