am 0e5c2beb: Merge "First commit of the system egl implementation."
* commit '0e5c2beb132d87f1190ab8d2aa9edde2563007f9': First commit of the system egl implementation.
This commit is contained in:
@@ -0,0 +1,22 @@
|
|||||||
|
#ifndef _SYSTEM_COMMON_EGL_CLIENT_IFACE_H
|
||||||
|
#define _SYSTEM_COMMON_EGL_CLIENT_IFACE_H
|
||||||
|
|
||||||
|
struct EGLThreadInfo; // defined in ThreadInfo.h
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
EGLThreadInfo* (*getThreadInfo)();
|
||||||
|
} EGLClient_eglInterface;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
void* (*getProcAddress)(const char *funcName);
|
||||||
|
} EGLClient_glesInterface;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Any GLES/GLES2 client API library should define a function named "init_emul_gles"
|
||||||
|
// with the following prototype,
|
||||||
|
// It will be called by EGL after loading the GLES library for initialization
|
||||||
|
// and exchanging interface function pointers.
|
||||||
|
//
|
||||||
|
typedef EGLClient_glesInterface *(*init_emul_gles_t)(EGLClient_eglInterface *eglIface);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -22,10 +22,11 @@ struct EGLContext_t;
|
|||||||
|
|
||||||
struct EGLThreadInfo
|
struct EGLThreadInfo
|
||||||
{
|
{
|
||||||
EGLThreadInfo() : currentContext(NULL), hostConn(NULL) {}
|
EGLThreadInfo() : currentContext(NULL), hostConn(NULL), eglError(0) {}
|
||||||
|
|
||||||
EGLContext_t *currentContext;
|
EGLContext_t *currentContext;
|
||||||
HostConnection *hostConn;
|
HostConnection *hostConn;
|
||||||
|
int eglError;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
50
tools/emulator/opengl/system/egl/Android.mk
Normal file
50
tools/emulator/opengl/system/egl/Android.mk
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
ifneq (,$(BUILD_EMULATOR_OPENGL_DRIVER))
|
||||||
|
|
||||||
|
LOCAL_PATH := $(call my-dir)
|
||||||
|
emulatorOpengl := $(LOCAL_PATH)/../..
|
||||||
|
|
||||||
|
### EGL implementation ###########################################
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
# add additional depencies to ensure that the generated code that we depend on
|
||||||
|
# is generated
|
||||||
|
LOCAL_ADDITIONAL_DEPENDENCIES := \
|
||||||
|
$(TARGET_OUT_SHARED_LIBRARIES)/lib_renderControl_enc$(TARGET_SHLIB_SUFFIX) \
|
||||||
|
$(TARGET_OUT_SHARED_LIBRARIES)/libGLESv1_enc$(TARGET_SHLIB_SUFFIX)
|
||||||
|
|
||||||
|
LOCAL_SRC_FILES := \
|
||||||
|
eglDisplay.cpp \
|
||||||
|
egl.cpp
|
||||||
|
|
||||||
|
|
||||||
|
LOCAL_PRELINK_MODULE := false
|
||||||
|
LOCAL_CFLAGS += -DLOG_TAG=\"EGL_emulation\" -DEGL_EGLEXT_PROTOTYPES
|
||||||
|
LOCAL_C_INCLUDES += \
|
||||||
|
$(emulatorOpengl)/host/include/libOpenglRender \
|
||||||
|
$(emulatorOpengl)/shared/OpenglCodecCommon \
|
||||||
|
$(emulatorOpengl)/system/OpenglSystemCommon \
|
||||||
|
$(emulatorOpengl)/system/GLESv1_enc \
|
||||||
|
$(emulatorOpengl)/system/renderControl_enc \
|
||||||
|
$(call intermediates-dir-for, SHARED_LIBRARIES, lib_renderControl_enc) \
|
||||||
|
$(call intermediates-dir-for, SHARED_LIBRARIES, libGLESv1_enc)
|
||||||
|
|
||||||
|
LOCAL_MODULE_TAGS := debug
|
||||||
|
LOCAL_MODULE := libEGL_emulation
|
||||||
|
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
|
||||||
|
|
||||||
|
|
||||||
|
LOCAL_STATIC_LIBRARIES := \
|
||||||
|
libOpenglSystemCommon \
|
||||||
|
libOpenglCodecCommon
|
||||||
|
|
||||||
|
LOCAL_SHARED_LIBRARIES := \
|
||||||
|
libcutils \
|
||||||
|
libutils \
|
||||||
|
libdl \
|
||||||
|
libGLESv1_enc \
|
||||||
|
lib_renderControl_enc
|
||||||
|
|
||||||
|
|
||||||
|
include $(BUILD_SHARED_LIBRARY)
|
||||||
|
|
||||||
|
endif # of ifneq (,$(BUILD_EMULATOR_OPENGL_DRIVER))
|
||||||
387
tools/emulator/opengl/system/egl/egl.cpp
Normal file
387
tools/emulator/opengl/system/egl/egl.cpp
Normal file
@@ -0,0 +1,387 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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 "HostConnection.h"
|
||||||
|
#include "ThreadInfo.h"
|
||||||
|
#include "eglDisplay.h"
|
||||||
|
#include "egl_ftable.h"
|
||||||
|
#include <cutils/log.h>
|
||||||
|
|
||||||
|
// The one and only supported display object.
|
||||||
|
static eglDisplay s_display;
|
||||||
|
|
||||||
|
static EGLClient_eglInterface s_eglIface = {
|
||||||
|
getThreadInfo: getEGLThreadInfo
|
||||||
|
};
|
||||||
|
|
||||||
|
#define RETURN_ERROR(ret,err) \
|
||||||
|
getEGLThreadInfo()->eglError = err; \
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
#define VALIDATE_CONFIG(cfg,ret) \
|
||||||
|
if(((int)cfg<0)||((int)cfg>s_display.getNumConfigs())) { \
|
||||||
|
RETURN_ERROR(ret,EGL_BAD_CONFIG); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define VALIDATE_DISPLAY(dpy,ret) \
|
||||||
|
if ((dpy) != (EGLDisplay)&s_display) { \
|
||||||
|
getEGLThreadInfo()->eglError = EGL_BAD_DISPLAY; \
|
||||||
|
return ret; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define VALIDATE_DISPLAY_INIT(dpy,ret) \
|
||||||
|
VALIDATE_DISPLAY(dpy, ret) \
|
||||||
|
if (!s_display.initialized()) { \
|
||||||
|
getEGLThreadInfo()->eglError = EGL_NOT_INITIALIZED; \
|
||||||
|
return ret; \
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLDisplay eglGetDisplay(EGLNativeDisplayType display_id)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// we support only EGL_DEFAULT_DISPLAY.
|
||||||
|
//
|
||||||
|
if (display_id != EGL_DEFAULT_DISPLAY) {
|
||||||
|
return EGL_NO_DISPLAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (EGLDisplay)&s_display;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor)
|
||||||
|
{
|
||||||
|
VALIDATE_DISPLAY(dpy,EGL_FALSE);
|
||||||
|
|
||||||
|
if (!s_display.initialize(&s_eglIface)) {
|
||||||
|
return EGL_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
*major = s_display.getVersionMajor();
|
||||||
|
*minor = s_display.getVersionMinor();
|
||||||
|
return EGL_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLBoolean eglTerminate(EGLDisplay dpy)
|
||||||
|
{
|
||||||
|
VALIDATE_DISPLAY_INIT(dpy, EGL_FALSE);
|
||||||
|
|
||||||
|
s_display.terminate();
|
||||||
|
return EGL_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLint eglGetError()
|
||||||
|
{
|
||||||
|
return getEGLThreadInfo()->eglError;
|
||||||
|
}
|
||||||
|
|
||||||
|
__eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname)
|
||||||
|
{
|
||||||
|
// search in EGL function table
|
||||||
|
for (int i=0; i<egl_num_funcs; i++) {
|
||||||
|
if (!strcmp(egl_funcs_by_name[i].name, procname)) {
|
||||||
|
return (__eglMustCastToProperFunctionPointerType)egl_funcs_by_name[i].proc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Make sure display is initialized before searching in client APIs
|
||||||
|
//
|
||||||
|
if (!s_display.initialized()) {
|
||||||
|
if (!s_display.initialize(&s_eglIface)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// look in gles
|
||||||
|
void *proc = s_display.gles_iface()->getProcAddress( procname );
|
||||||
|
if (proc != NULL) {
|
||||||
|
return (__eglMustCastToProperFunctionPointerType)proc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// look in gles2
|
||||||
|
if (s_display.gles2_iface() != NULL) {
|
||||||
|
proc = s_display.gles2_iface()->getProcAddress( procname );
|
||||||
|
if (proc != NULL) {
|
||||||
|
return (__eglMustCastToProperFunctionPointerType)proc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fail - function not found.
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* eglQueryString(EGLDisplay dpy, EGLint name)
|
||||||
|
{
|
||||||
|
VALIDATE_DISPLAY_INIT(dpy, NULL);
|
||||||
|
|
||||||
|
return s_display.queryString(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLBoolean eglGetConfigs(EGLDisplay dpy, EGLConfig *configs, EGLint config_size, EGLint *num_config)
|
||||||
|
{
|
||||||
|
VALIDATE_DISPLAY_INIT(dpy, NULL);
|
||||||
|
|
||||||
|
if(!num_config) {
|
||||||
|
RETURN_ERROR(EGL_FALSE,EGL_BAD_PARAMETER);
|
||||||
|
}
|
||||||
|
|
||||||
|
GLint numConfigs = s_display.getNumConfigs();
|
||||||
|
if (!configs) {
|
||||||
|
*num_config = numConfigs;
|
||||||
|
return EGL_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i=0;
|
||||||
|
for (i=0 ; i<numConfigs && i<config_size ; i++) {
|
||||||
|
*configs++ = (EGLConfig)i;
|
||||||
|
}
|
||||||
|
*num_config = i;
|
||||||
|
return EGL_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config)
|
||||||
|
{
|
||||||
|
VALIDATE_DISPLAY_INIT(dpy, NULL);
|
||||||
|
|
||||||
|
//TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint *value)
|
||||||
|
{
|
||||||
|
VALIDATE_DISPLAY_INIT(dpy, NULL);
|
||||||
|
VALIDATE_CONFIG(config, EGL_FALSE);
|
||||||
|
|
||||||
|
EGLint attribVal = s_display.getConfigAttrib(config, attribute);
|
||||||
|
|
||||||
|
if (attribVal == ATTRIBUTE_NONE) {
|
||||||
|
RETURN_ERROR(EGL_FALSE, EGL_BAD_ATTRIBUTE);
|
||||||
|
}
|
||||||
|
|
||||||
|
*value = attribVal;
|
||||||
|
return EGL_TRUE;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list)
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLSurface eglCreatePbufferSurface(EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list)
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLSurface eglCreatePixmapSurface(EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, const EGLint *attrib_list)
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface)
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLBoolean eglQuerySurface(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint *value)
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLBoolean eglBindAPI(EGLenum api)
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLenum eglQueryAPI()
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLBoolean eglWaitClient()
|
||||||
|
{ //TODO
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLBoolean eglReleaseThread()
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLSurface eglCreatePbufferFromClientBuffer(EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, EGLConfig config, const EGLint *attrib_list)
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLBoolean eglSurfaceAttrib(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value)
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLBoolean eglBindTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer)
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLBoolean eglReleaseTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer)
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval)
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list)
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx)
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLBoolean eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx)
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLContext eglGetCurrentContext()
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLSurface eglGetCurrentSurface(EGLint readdraw)
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLDisplay eglGetCurrentDisplay()
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLBoolean eglQueryContext(EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint *value)
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLBoolean eglWaitGL()
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLBoolean eglWaitNative(EGLint engine)
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface)
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLBoolean eglCopyBuffers(EGLDisplay dpy, EGLSurface surface, EGLNativePixmapType target)
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLBoolean eglLockSurfaceKHR(EGLDisplay display, EGLSurface surface, const EGLint *attrib_list)
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLBoolean eglUnlockSurfaceKHR(EGLDisplay display, EGLSurface surface)
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list)
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR image)
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLSyncKHR eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint *attrib_list)
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLBoolean eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync)
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout)
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLBoolean eglSignalSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLenum mode)
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLBoolean eglGetSyncAttribKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, EGLint *value)
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLBoolean eglSetSwapRectangleANDROID(EGLDisplay dpy, EGLSurface draw, EGLint left, EGLint top, EGLint width, EGLint height)
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
376
tools/emulator/opengl/system/egl/eglDisplay.cpp
Normal file
376
tools/emulator/opengl/system/egl/eglDisplay.cpp
Normal file
@@ -0,0 +1,376 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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 "eglDisplay.h"
|
||||||
|
#include "HostConnection.h"
|
||||||
|
#include <dlfcn.h>
|
||||||
|
|
||||||
|
static const int systemEGLVersionMajor = 1;
|
||||||
|
static const int systemEGLVersionMinor = 4;
|
||||||
|
static const char *systemEGLVendor = "Google Android emulator";
|
||||||
|
|
||||||
|
// list of extensions supported by this EGL implementation
|
||||||
|
// NOTE that each extension name should be suffixed with space
|
||||||
|
static const char *systemStaticEGLExtensions =
|
||||||
|
"EGL_ANDROID_image_native_buffer ";
|
||||||
|
|
||||||
|
// list of extensions supported by this EGL implementation only if supported
|
||||||
|
// on the host implementation.
|
||||||
|
// NOTE that each extension name should be suffixed with space
|
||||||
|
static const char *systemDynamicEGLExtensions =
|
||||||
|
"EGL_KHR_image_base "
|
||||||
|
"EGL_KHR_gl_texture_2d_image ";
|
||||||
|
|
||||||
|
|
||||||
|
static void *s_gles_lib = NULL;
|
||||||
|
static void *s_gles2_lib = NULL;
|
||||||
|
|
||||||
|
// The following function will be called when we (libEGL)
|
||||||
|
// gets unloaded
|
||||||
|
// At this point we want to unload the gles libraries we
|
||||||
|
// might have loaded during initialization
|
||||||
|
static void __attribute__ ((destructor)) do_on_unload(void)
|
||||||
|
{
|
||||||
|
if (s_gles_lib) {
|
||||||
|
dlclose(s_gles_lib);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s_gles2_lib) {
|
||||||
|
dlclose(s_gles2_lib);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eglDisplay::eglDisplay() :
|
||||||
|
m_initialized(false),
|
||||||
|
m_major(0),
|
||||||
|
m_minor(0),
|
||||||
|
m_hostRendererVersion(0),
|
||||||
|
m_numConfigs(0),
|
||||||
|
m_numConfigAttribs(0),
|
||||||
|
m_attribs(DefaultKeyedVector<EGLint, EGLint>(ATTRIBUTE_NONE)),
|
||||||
|
m_configs(NULL),
|
||||||
|
m_gles_iface(NULL),
|
||||||
|
m_gles2_iface(NULL),
|
||||||
|
m_versionString(NULL),
|
||||||
|
m_vendorString(NULL),
|
||||||
|
m_extensionString(NULL)
|
||||||
|
{
|
||||||
|
pthread_mutex_init(&m_lock, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool eglDisplay::initialize(EGLClient_eglInterface *eglIface)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&m_lock);
|
||||||
|
if (!m_initialized) {
|
||||||
|
|
||||||
|
//
|
||||||
|
// load GLES client API
|
||||||
|
//
|
||||||
|
m_gles_iface = loadGLESClientAPI("/system/lib/egl/libGLESv1_CM_emulation.so",
|
||||||
|
eglIface,
|
||||||
|
&s_gles_lib);
|
||||||
|
if (!m_gles_iface) {
|
||||||
|
pthread_mutex_unlock(&m_lock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WITH_GLES2
|
||||||
|
m_gles2_iface = loadGLESClientAPI("/system/lib/egl/libGLESv2_emulation.so",
|
||||||
|
eglIface,
|
||||||
|
&s_gles2_lib);
|
||||||
|
// Note that if loading gles2 failed, we can still run with no
|
||||||
|
// GLES2 support, having GLES2 is not mandatory.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//
|
||||||
|
// establish connection with the host
|
||||||
|
//
|
||||||
|
HostConnection *hcon = HostConnection::get();
|
||||||
|
if (!hcon) {
|
||||||
|
pthread_mutex_unlock(&m_lock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// get renderControl encoder instance
|
||||||
|
//
|
||||||
|
renderControl_encoder_context_t *rcEnc = hcon->rcEncoder();
|
||||||
|
if (!rcEnc) {
|
||||||
|
pthread_mutex_unlock(&m_lock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Query host reneder and EGL version
|
||||||
|
//
|
||||||
|
m_hostRendererVersion = rcEnc->rcGetRendererVersion(rcEnc);
|
||||||
|
EGLint status = rcEnc->rcGetEGLVersion(rcEnc, &m_major, &m_minor);
|
||||||
|
if (status != EGL_TRUE) {
|
||||||
|
// host EGL initialization failed !!
|
||||||
|
pthread_mutex_unlock(&m_lock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Take minimum version beween what we support and what the host support
|
||||||
|
//
|
||||||
|
if (m_major > systemEGLVersionMajor) {
|
||||||
|
m_major = systemEGLVersionMajor;
|
||||||
|
m_minor = systemEGLVersionMinor;
|
||||||
|
}
|
||||||
|
else if (m_major == systemEGLVersionMajor &&
|
||||||
|
m_minor > systemEGLVersionMinor) {
|
||||||
|
m_minor = systemEGLVersionMinor;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Query the host for the set of configs
|
||||||
|
//
|
||||||
|
m_numConfigs = rcEnc->rcGetNumConfigs(rcEnc, (uint32_t*)&m_numConfigAttribs);
|
||||||
|
if (m_numConfigs <= 0 || m_numConfigAttribs <= 0) {
|
||||||
|
// just sanity check - should never happen
|
||||||
|
pthread_mutex_unlock(&m_lock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t nInts = m_numConfigAttribs * (m_numConfigs + 1);
|
||||||
|
EGLint tmp_buf[nInts];
|
||||||
|
m_configs = new EGLint[nInts-1];
|
||||||
|
if (!m_configs) {
|
||||||
|
pthread_mutex_unlock(&m_lock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//EGLint n = rcEnc->rcGetConfigs(rcEnc, nInts*sizeof(EGLint), m_configs);
|
||||||
|
EGLint n = rcEnc->rcGetConfigs(rcEnc, nInts*sizeof(EGLint), (GLuint*)tmp_buf);
|
||||||
|
if (n != m_numConfigs) {
|
||||||
|
pthread_mutex_unlock(&m_lock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Fill the attributes vector.
|
||||||
|
//The first m_numConfigAttribs values of tmp_buf are the actual attributes enums.
|
||||||
|
for (int i=0; i<m_numConfigAttribs; i++)
|
||||||
|
{
|
||||||
|
m_attribs.add(tmp_buf[i], i);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Copy the actual configs data to m_configs
|
||||||
|
memcpy(m_configs, tmp_buf + m_numConfigAttribs*sizeof(EGLint), m_numConfigs*sizeof(EGLint));
|
||||||
|
|
||||||
|
m_initialized = true;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&m_lock);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void eglDisplay::terminate()
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&m_lock);
|
||||||
|
if (m_initialized) {
|
||||||
|
m_initialized = false;
|
||||||
|
delete [] m_configs;
|
||||||
|
m_configs = NULL;
|
||||||
|
|
||||||
|
if (m_versionString) {
|
||||||
|
free(m_versionString);
|
||||||
|
m_versionString = NULL;
|
||||||
|
}
|
||||||
|
if (m_vendorString) {
|
||||||
|
free(m_vendorString);
|
||||||
|
m_vendorString = NULL;
|
||||||
|
}
|
||||||
|
if (m_extensionString) {
|
||||||
|
free(m_extensionString);
|
||||||
|
m_extensionString = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&m_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLClient_glesInterface *eglDisplay::loadGLESClientAPI(const char *libName,
|
||||||
|
EGLClient_eglInterface *eglIface,
|
||||||
|
void **libHandle)
|
||||||
|
{
|
||||||
|
void *lib = dlopen(libName, RTLD_NOW);
|
||||||
|
if (!lib) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
init_emul_gles_t init_gles_func = (init_emul_gles_t)dlsym(lib,"init_emul_gles");
|
||||||
|
if (!init_gles_func) {
|
||||||
|
dlclose((void*)libName);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*libHandle = lib;
|
||||||
|
return (*init_gles_func)(eglIface);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *queryHostEGLString(EGLint name)
|
||||||
|
{
|
||||||
|
HostConnection *hcon = HostConnection::get();
|
||||||
|
if (hcon) {
|
||||||
|
renderControl_encoder_context_t *rcEnc = hcon->rcEncoder();
|
||||||
|
if (rcEnc) {
|
||||||
|
int n = rcEnc->rcQueryEGLString(rcEnc, name, NULL, 0);
|
||||||
|
if (n < 0) {
|
||||||
|
// allocate space for the string with additional
|
||||||
|
// space charachter to be suffixed at the end.
|
||||||
|
char *str = (char *)malloc(-n+2);
|
||||||
|
n = rcEnc->rcQueryEGLString(rcEnc, name, str, -n);
|
||||||
|
if (n > 0) {
|
||||||
|
// add extra space at end of string which will be
|
||||||
|
// needed later when filtering the extension list.
|
||||||
|
strcat(str, " ");
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool findExtInList(const char* token, int tokenlen, const char* list)
|
||||||
|
{
|
||||||
|
const char* p = list;
|
||||||
|
while (*p != '\0') {
|
||||||
|
const char* q = strchr(p, ' ');
|
||||||
|
if (q == NULL) {
|
||||||
|
/* should not happen, list must be space-terminated */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (tokenlen == (q - p) && !memcmp(token, p, tokenlen)) {
|
||||||
|
return true; /* found it */
|
||||||
|
}
|
||||||
|
p = q+1;
|
||||||
|
}
|
||||||
|
return false; /* not found */
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *buildExtensionString()
|
||||||
|
{
|
||||||
|
//Query host extension string
|
||||||
|
char *hostExt = queryHostEGLString(EGL_EXTENSIONS);
|
||||||
|
if (!hostExt || (hostExt[1] == '\0')) {
|
||||||
|
// no extensions on host - only static extension list supported
|
||||||
|
return strdup(systemStaticEGLExtensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Filter host extension list to include only extensions
|
||||||
|
// we can support (in the systemDynamicEGLExtensions list)
|
||||||
|
//
|
||||||
|
char *ext = (char *)hostExt;
|
||||||
|
char *c = ext;
|
||||||
|
char *insert = ext;
|
||||||
|
while(*c != '\0') {
|
||||||
|
if (*c == ' ') {
|
||||||
|
int len = c - ext;
|
||||||
|
if (findExtInList(ext, len, systemDynamicEGLExtensions)) {
|
||||||
|
if (ext != insert) {
|
||||||
|
memcpy(insert, ext, len+1); // including space
|
||||||
|
}
|
||||||
|
insert += (len + 1);
|
||||||
|
}
|
||||||
|
ext = c + 1;
|
||||||
|
}
|
||||||
|
c++;
|
||||||
|
}
|
||||||
|
*insert = '\0';
|
||||||
|
|
||||||
|
int n = strlen(hostExt);
|
||||||
|
if (n > 0) {
|
||||||
|
char *str;
|
||||||
|
asprintf(&str,"%s%s", systemStaticEGLExtensions, hostExt);
|
||||||
|
free((char*)hostExt);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
free((char*)hostExt);
|
||||||
|
return strdup(systemStaticEGLExtensions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *eglDisplay::queryString(EGLint name)
|
||||||
|
{
|
||||||
|
if (name == EGL_CLIENT_APIS) {
|
||||||
|
return "OpenGL_ES";
|
||||||
|
}
|
||||||
|
else if (name == EGL_VERSION) {
|
||||||
|
pthread_mutex_lock(&m_lock);
|
||||||
|
if (m_versionString) {
|
||||||
|
pthread_mutex_unlock(&m_lock);
|
||||||
|
return m_versionString;
|
||||||
|
}
|
||||||
|
|
||||||
|
// build version string
|
||||||
|
asprintf(&m_versionString, "%d.%d", m_major, m_minor);
|
||||||
|
pthread_mutex_unlock(&m_lock);
|
||||||
|
|
||||||
|
return m_versionString;
|
||||||
|
}
|
||||||
|
else if (name == EGL_VENDOR) {
|
||||||
|
pthread_mutex_lock(&m_lock);
|
||||||
|
if (m_vendorString) {
|
||||||
|
pthread_mutex_unlock(&m_lock);
|
||||||
|
return m_vendorString;
|
||||||
|
}
|
||||||
|
|
||||||
|
// build vendor string
|
||||||
|
const char *hostVendor = queryHostEGLString(EGL_VENDOR);
|
||||||
|
|
||||||
|
if (hostVendor) {
|
||||||
|
asprintf(&m_vendorString, "%s Host: %s",
|
||||||
|
systemEGLVendor, hostVendor);
|
||||||
|
free((char*)hostVendor);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_vendorString = (char *)systemEGLVendor;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&m_lock);
|
||||||
|
|
||||||
|
return m_vendorString;
|
||||||
|
}
|
||||||
|
else if (name == EGL_EXTENSIONS) {
|
||||||
|
pthread_mutex_lock(&m_lock);
|
||||||
|
if (m_extensionString) {
|
||||||
|
pthread_mutex_unlock(&m_lock);
|
||||||
|
return m_extensionString;
|
||||||
|
}
|
||||||
|
|
||||||
|
// build extension string
|
||||||
|
m_extensionString = buildExtensionString();
|
||||||
|
pthread_mutex_unlock(&m_lock);
|
||||||
|
|
||||||
|
return m_extensionString;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LOGE("[%s] Unknown name %d\n", __FUNCTION__, name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLint eglDisplay::getConfigAttrib(EGLConfig config, EGLint attrib)
|
||||||
|
{
|
||||||
|
EGLint attrIdx = m_attribs.valueFor(attrib);
|
||||||
|
|
||||||
|
if (attrIdx == ATTRIBUTE_NONE) return ATTRIBUTE_NONE;
|
||||||
|
else return *(m_configs + (int)config*m_numConfigAttribs + attrIdx);
|
||||||
|
}
|
||||||
71
tools/emulator/opengl/system/egl/eglDisplay.h
Normal file
71
tools/emulator/opengl/system/egl/eglDisplay.h
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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.
|
||||||
|
*/
|
||||||
|
#ifndef _SYSTEM_EGL_DISPLAY_H
|
||||||
|
#define _SYSTEM_EGL_DISPLAY_H
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <EGL/egl.h>
|
||||||
|
#include <EGL/eglext.h>
|
||||||
|
#include "EGLClientIface.h"
|
||||||
|
#include <utils/KeyedVector.h>
|
||||||
|
|
||||||
|
#define ATTRIBUTE_NONE -1
|
||||||
|
//FIXME: are we in this namespace?
|
||||||
|
using namespace android;
|
||||||
|
|
||||||
|
class eglDisplay
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
eglDisplay();
|
||||||
|
|
||||||
|
bool initialize(EGLClient_eglInterface *eglIface);
|
||||||
|
void terminate();
|
||||||
|
|
||||||
|
int getVersionMajor() const { return m_major; }
|
||||||
|
int getVersionMinor() const { return m_minor; }
|
||||||
|
bool initialized() const { return m_initialized; }
|
||||||
|
|
||||||
|
const char *queryString(EGLint name);
|
||||||
|
|
||||||
|
const EGLClient_glesInterface *gles_iface() const { return m_gles_iface; }
|
||||||
|
const EGLClient_glesInterface *gles2_iface() const { return m_gles2_iface; }
|
||||||
|
|
||||||
|
int getNumConfigs(){ return m_numConfigs; }
|
||||||
|
EGLint getConfigAttrib(EGLConfig config, EGLint attrib);
|
||||||
|
|
||||||
|
private:
|
||||||
|
EGLClient_glesInterface *loadGLESClientAPI(const char *libName,
|
||||||
|
EGLClient_eglInterface *eglIface,
|
||||||
|
void **libHandle);
|
||||||
|
|
||||||
|
private:
|
||||||
|
pthread_mutex_t m_lock;
|
||||||
|
bool m_initialized;
|
||||||
|
int m_major;
|
||||||
|
int m_minor;
|
||||||
|
int m_hostRendererVersion;
|
||||||
|
int m_numConfigs;
|
||||||
|
int m_numConfigAttribs;
|
||||||
|
DefaultKeyedVector<EGLint, EGLint> m_attribs;
|
||||||
|
EGLint *m_configs;
|
||||||
|
EGLClient_glesInterface *m_gles_iface;
|
||||||
|
EGLClient_glesInterface *m_gles2_iface;
|
||||||
|
char *m_versionString;
|
||||||
|
char *m_vendorString;
|
||||||
|
char *m_extensionString;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
66
tools/emulator/opengl/system/egl/egl_ftable.h
Normal file
66
tools/emulator/opengl/system/egl/egl_ftable.h
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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.
|
||||||
|
*/
|
||||||
|
static struct _egl_funcs_by_name {
|
||||||
|
const char *name;
|
||||||
|
void *proc;
|
||||||
|
} egl_funcs_by_name[] = {
|
||||||
|
{"eglGetError", (void *)eglGetError},
|
||||||
|
{"eglGetDisplay", (void *)eglGetDisplay},
|
||||||
|
{"eglInitialize", (void *)eglInitialize},
|
||||||
|
{"eglTerminate", (void *)eglTerminate},
|
||||||
|
{"eglQueryString", (void *)eglQueryString},
|
||||||
|
{"eglGetConfigs", (void *)eglGetConfigs},
|
||||||
|
{"eglChooseConfig", (void *)eglChooseConfig},
|
||||||
|
{"eglGetConfigAttrib", (void *)eglGetConfigAttrib},
|
||||||
|
{"eglCreateWindowSurface", (void *)eglCreateWindowSurface},
|
||||||
|
{"eglCreatePbufferSurface", (void *)eglCreatePbufferSurface},
|
||||||
|
{"eglCreatePixmapSurface", (void *)eglCreatePixmapSurface},
|
||||||
|
{"eglDestroySurface", (void *)eglDestroySurface},
|
||||||
|
{"eglQuerySurface", (void *)eglQuerySurface},
|
||||||
|
{"eglBindAPI", (void *)eglBindAPI},
|
||||||
|
{"eglQueryAPI", (void *)eglQueryAPI},
|
||||||
|
{"eglWaitClient", (void *)eglWaitClient},
|
||||||
|
{"eglReleaseThread", (void *)eglReleaseThread},
|
||||||
|
{"eglCreatePbufferFromClientBuffer", (void *)eglCreatePbufferFromClientBuffer},
|
||||||
|
{"eglSurfaceAttrib", (void *)eglSurfaceAttrib},
|
||||||
|
{"eglBindTexImage", (void *)eglBindTexImage},
|
||||||
|
{"eglReleaseTexImage", (void *)eglReleaseTexImage},
|
||||||
|
{"eglSwapInterval", (void *)eglSwapInterval},
|
||||||
|
{"eglCreateContext", (void *)eglCreateContext},
|
||||||
|
{"eglDestroyContext", (void *)eglDestroyContext},
|
||||||
|
{"eglMakeCurrent", (void *)eglMakeCurrent},
|
||||||
|
{"eglGetCurrentContext", (void *)eglGetCurrentContext},
|
||||||
|
{"eglGetCurrentSurface", (void *)eglGetCurrentSurface},
|
||||||
|
{"eglGetCurrentDisplay", (void *)eglGetCurrentDisplay},
|
||||||
|
{"eglQueryContext", (void *)eglQueryContext},
|
||||||
|
{"eglWaitGL", (void *)eglWaitGL},
|
||||||
|
{"eglWaitNative", (void *)eglWaitNative},
|
||||||
|
{"eglSwapBuffers", (void *)eglSwapBuffers},
|
||||||
|
{"eglCopyBuffers", (void *)eglCopyBuffers},
|
||||||
|
{"eglGetProcAddress", (void *)eglGetProcAddress},
|
||||||
|
{"eglLockSurfaceKHR", (void *)eglLockSurfaceKHR},
|
||||||
|
{"eglUnlockSurfaceKHR", (void *)eglUnlockSurfaceKHR},
|
||||||
|
{"eglCreateImageKHR", (void *)eglCreateImageKHR},
|
||||||
|
{"eglDestroyImageKHR", (void *)eglDestroyImageKHR},
|
||||||
|
{"eglCreateSyncKHR", (void *)eglCreateSyncKHR},
|
||||||
|
{"eglDestroySyncKHR", (void *)eglDestroySyncKHR},
|
||||||
|
{"eglClientWaitSyncKHR", (void *)eglClientWaitSyncKHR},
|
||||||
|
{"eglSignalSyncKHR", (void *)eglSignalSyncKHR},
|
||||||
|
{"eglGetSyncAttribKHR", (void *)eglGetSyncAttribKHR},
|
||||||
|
{"eglSetSwapRectangleANDROID", (void *)eglSetSwapRectangleANDROID}
|
||||||
|
};
|
||||||
|
|
||||||
|
static int egl_num_funcs = sizeof(egl_funcs_by_name) / sizeof(struct _egl_funcs_by_name);
|
||||||
Reference in New Issue
Block a user