1

#define GL_ENTRY(_r, _api, ...) _r (*_api)(__VA_ARGS__);

可以看到 struct gl_hooks_t 的 struct gl_t gl 的所有成员都是函数指针,即它是一个函数表,一个 OpenGL 接口函数的函数表。

上面看到的 struct egl_t 与 struct gl_hooks_t 的 struct gl_t gl 定义类似,只是它的结构体成员来自于另外一个文件 frameworks/native/opengl/libs/EGL/egl_entries.in

1

2

3

4

5

6

EGL_ENTRY(EGLDisplay, eglGetDisplay, NativeDisplayType)

EGL_ENTRY(EGLBoolean, eglInitialize, EGLDisplay, EGLint*, EGLint*)

EGL_ENTRY(EGLBoolean, eglTerminate, EGLDisplay)

EGL_ENTRY(EGLBoolean, eglGetConfigs, EGLDisplay, EGLConfig*, EGLint, EGLint*)

EGL_ENTRY(EGLBoolean, eglChooseConfig, EGLDisplay, const EGLint *, EGLConfig *, EGLint, EGLint *)

. . . . . .

EGL_ENTRY 宏的定义与 GL_ENTRY 宏的完全相同。struct egl_t 同样为一个函数表,只是它是 EGL 接口的函数表。

再来看 egl_connection_t 的定义,位于 frameworks/native/opengl/libs/EGL/egldefs.h

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

struct egl_connection_t {

enum {

GLESv1_INDEX = 0,

GLESv2_INDEX = 1

};

inline egl_connection_t() : dso(0) { }

void * dso;

gl_hooks_t * hooks[2];

EGLint major;

EGLint minor;

egl_t egl;

void* libEgl;

void* libGles1;

void* libGles2;

};

这个结构包含了主、次版本号; 4 个指针;两个函数表的指针以及一个函数表。

Loader::open(egl_connection_t* cnx) 初始化图形驱动,主要是初始化这些函数表和指针。Loader::open(egl_connection_t* cnx) 的定义如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

static void* load_wrapper(const char* path) {

void* so = dlopen(path, RTLD_NOW | RTLD_LOCAL);

ALOGE_IF(!so, "dlopen(\"%s\") failed: %s", path, dlerror());

return so;

}

#ifndef EGL_WRAPPER_DIR

#if defined(__LP64__)

#define EGL_WRAPPER_DIR "/system/lib64"

#else

#define EGL_WRAPPER_DIR "/system/lib"

#endif

#endif

static void setEmulatorGlesValue(void) {

char prop[PROPERTY_VALUE_MAX];

property_get("ro.kernel.qemu", prop, "0");

if (atoi(prop) != 1) return;

property_get("ro.kernel.qemu.gles",prop,"0");

if (atoi(prop) == 1) {

ALOGD("Emulator has host GPU support, qemu.gles is set to 1.");

property_set("qemu.gles", "1");

return;

}

// for now, checking the following

// directory is good enough for emulator system images

const char* vendor_lib_path =

#if defined(__LP64__)

"/vendor/lib64/egl";

#else

"/vendor/lib/egl";

#endif

const bool has_vendor_lib = (access(vendor_lib_path, R_OK) == 0);

if (has_vendor_lib) {

ALOGD("Emulator has vendor provided software renderer, qemu.gles is set to 2.");

property_set("qemu.gles", "2");

} else {

ALOGD("Emulator without GPU support detected. "

"Fallback to legacy software renderer, qemu.gles is set to 0.");

property_set("qemu.gles", "0");

}

}

void* Loader::open(egl_connection_t* cnx)

{

void* dso;

driver_t* hnd = 0;

setEmulatorGlesValue();

dso = load_driver("GLES", cnx, EGL | GLESv1_CM | GLESv2);

if (dso) {

hnd = new driver_t(dso);

} else {

// Always load EGL first

dso = load_driver("EGL", cnx, EGL);

if (dso) {

hnd = new driver_t(dso);

hnd->set( load_driver("GLESv1_CM", cnx, GLESv1_CM), GLESv1_CM );

hnd->set( load_driver("GLESv2", cnx, GLESv2), GLESv2 );

}

}

LOG_ALWAYS_FATAL_IF(!hnd, "couldn't find an OpenGL ES implementation");

cnx->libEgl = load_wrapper(EGL_WRAPPER_DIR "/libEGL.so");

cnx->libGles2 = load_wrapper(EGL_WRAPPER_DIR "/libGLESv2.so");

cnx->libGles1 = load_wrapper(EGL_WRAPPER_DIR "/libGLESv1_CM.so");

LOG_ALWAYS_FATAL_IF(!cnx->libEgl,

"couldn't load system EGL wrapper libraries");

LOG_ALWAYS_FATAL_IF(!cnx->libGles2 || !cnx->libGles1,

"couldn't load system OpenGL ES wrapper libraries");

return (void*)hnd;

}

这个函数中,执行步骤如下:

  1. 为设备是模拟器的情况,设置系统属性 qemu.gles

  2. 加载设备特有的图形驱动库,包括 EGL 库,OpenGL ES 1.0 和 2.0 的库。

  3. 加载图形驱动 Wrapper,它们都位于 /system/lib64 或 /system/lib

egl_connection_t 中的几个指针中,dso 实际为 struct driver_t 指针,该结构定义(位于 frameworks/native/opengl/libs/EGL/Loader.h)如下:

1

2

3

4

5

6

struct driver_t {

driver_t(void* gles);

~driver_t();

status_t set(void* hnd, int32_t api);

void* dso[3];

};

struct driver_t 包含设备生产商提供的设备特有 EGL 和 OpenGL ES 实现库的句柄,如果 EGL 接口和 OpenGL 接口由单独的库实现,它包含一个库的句柄,即这个单独的库,如果 EGL 接口由不同的库实现,它则包含所有这些库的句柄。

egl_connection_t 的 libEgllibGles2 和 libGles1 为动态链接库句柄,其中 libEgl 指向 Android 的 EGL Wrapper 库;libGles1 指向 Android 的 GLESv1_CM Wrapper 库;libGles2 指向 Android 的 GLESv2 Wrapper 库。

然后来看 Loader::load_driver()

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

/* This function is called to check whether we run inside the emulator,

* and if this is the case whether GLES GPU emulation is supported.

*

* Returned values are:

* -1 -> not running inside the emulator

* 0 -> running inside the emulator, but GPU emulation not supported

* 1 -> running inside the emulator, GPU emulation is supported

* through the "emulation" host-side OpenGL ES implementation.

* 2 -> running inside the emulator, GPU emulation is supported

* through a guest-side vendor driver's OpenGL ES implementation.

*/

static int

checkGlesEmulationStatus(void)

{

/* We're going to check for the following kernel parameters:

*

* qemu=1 -> tells us that we run inside the emulator

* android.qemu.gles=<number> -> tells us the GLES GPU emulation status

*

* Note that we will return <number> if we find it. This let us support

* more additionnal emulation modes in the future.

*/

char prop[PROPERTY_VALUE_MAX];

int result = -1;

/* First, check for qemu=1 */

property_get("ro.kernel.qemu",prop,"0");

if (atoi(prop) != 1)

return -1;

/* We are in the emulator, get GPU status value */

property_get("qemu.gles",prop,"0");

return atoi(prop);

}

. . . . . .

void Loader::init_api(void* dso,

char const * const * api,

__eglMustCastToProperFunctionPointerType* curr,

getProcAddressType getProcAddress)

{

const ssize_t SIZE = 256;

char scrap[SIZE];

while (*api) {

char const * name = *api;

__eglMustCastToProperFunctionPointerType f =

(__eglMustCastToProperFunctionPointerType)dlsym(dso, name);

if (f == NULL) {

// couldn't find the entry-point, use eglGetProcAddress()

f = getProcAddress(name);

}

if (f == NULL) {

// Try without the OES postfix

ssize_t index = ssize_t(strlen(name)) - 3;

if ((index>0 && (index<SIZE-1)) && (!strcmp(name+index, "OES"))) {

strncpy(scrap, name, index);

scrap[index] = 0;

f = (__eglMustCastToProperFunctionPointerType)dlsym(dso, scrap);

//ALOGD_IF(f, "found <%s> instead", scrap);

}

}

if (f == NULL) {

// Try with the OES postfix

ssize_t index = ssize_t(strlen(name)) - 3;

if (index>0 && strcmp(name+index, "OES")) {

snprintf(scrap, SIZE, "%sOES", name);

f = (__eglMustCastToProperFunctionPointerType)dlsym(dso, scrap);

//ALOGD_IF(f, "found <%s> instead", scrap);

}

}

if (f == NULL) {

//ALOGD("%s", name);

f = (__eglMustCastToProperFunctionPointerType)gl_unimplemented;

/*

* GL_EXT_debug_label is special, we always report it as

* supported, it's handled by GLES_trace. If GLES_trace is not

* enabled, then these are no-ops.

*/

if (!strcmp(name, "glInsertEventMarkerEXT")) {

f = (__eglMustCastToProperFunctionPointerType)gl_noop;

} else if (!strcmp(name, "glPushGroupMarkerEXT")) {

f = (__eglMustCastToProperFunctionPointerType)gl_noop;

} else if (!strcmp(name, "glPopGroupMarkerEXT")) {

f = (__eglMustCastToProperFunctionPointerType)gl_noop;

}

}

*curr++ = f;

api++;

}

}

void *Loader::load_driver(const char* kind,

egl_connection_t* cnx, uint32_t mask)

{

class MatchFile {

public:

static String8 find(const char* kind) {

String8 result;

int emulationStatus = checkGlesEmulationStatus();

switch (emulationStatus) {

case 0:

#if defined(__LP64__)

result.setTo("/system/lib64/egl/libGLES_android.so");

#else

result.setTo("/system/lib/egl/libGLES_android.so");

#endif

return result;

case 1:

// Use host-side OpenGL through the "emulation" library

#if defined(__LP64__)

result.appendFormat("/system/lib64/egl/lib%s_emulation.so", kind);

#else

result.appendFormat("/system/lib/egl/lib%s_emulation.so", kind);

#endif

return result;

default:

// Not in emulator, or use other guest-side implementation

break;

}

String8 pattern;

pattern.appendFormat("lib%s", kind);

const char* const searchPaths[] = {

#if defined(__LP64__)

"/vendor/lib64/egl",

"/system/lib64/egl"

#else

"/vendor/lib/egl",

"/system/lib/egl"

#endif

};

// first, we search for the exact name of the GLES userspace

// driver in both locations.

// i.e.:

// libGLES.so, or:

// libEGL.so, libGLESv1_CM.so, libGLESv2.so

for (size_t i=0 ; i<NELEM(searchPaths) ; i++) {

if (find(result, pattern, searchPaths[i], true)) {

return result;

}

}

// for compatibility with the old "egl.cfg" naming convention

// we look for files that match:

// libGLES_*.so, or:

// libEGL_*.so, libGLESv1_CM_*.so, libGLESv2_*.so

pattern.append("_");

for (size_t i=0 ; i<NELEM(searchPaths) ; i++) {

if (find(result, pattern, searchPaths[i], false)) {

return result;

}

}

// we didn't find the driver. gah.

result.clear();

return result;

}

private:

static bool find(String8& result,

const String8& pattern, const char* const search, bool exact) {

if (exact) {

String8 absolutePath;

absolutePath.appendFormat("%s/%s.so", search, pattern.string());

if (!access(absolutePath.string(), R_OK)) {

result = absolutePath;

return true;

}

return false;

}

DIR* d = opendir(search);

if (d != NULL) {

struct dirent cur;

struct dirent* e;

while (readdir_r(d, &cur, &e) == 0 && e) {

if (e->d_type == DT_DIR) {

continue;

}

if (!strcmp(e->d_name, "libGLES_android.so")) {

// always skip the software renderer

continue;

}

if (strstr(e->d_name, pattern.string()) == e->d_name) {

if (!strcmp(e->d_name + strlen(e->d_name) - 3, ".so")) {

result.clear();

result.appendFormat("%s/%s", search, e->d_name);

closedir(d);

return true;

}

}

}

closedir(d);

}

return false;

}

};

String8 absolutePath = MatchFile::find(kind);

if (absolutePath.isEmpty()) {

// this happens often, we don't want to log an error

return 0;

}

const char* const driver_absolute_path = absolutePath.string();

void* dso = dlopen(driver_absolute_path, RTLD_NOW | RTLD_LOCAL);

if (dso == 0) {

const char* err = dlerror();

ALOGE("load_driver(%s): %s", driver_absolute_path, err?err:"unknown");

return 0;

}

if (mask & EGL) {

ALOGD("EGL loaded %s", driver_absolute_path);

getProcAddress = (getProcAddressType)dlsym(dso, "eglGetProcAddress");

ALOGE_IF(!getProcAddress,

"can't find eglGetProcAddress() in %s", driver_absolute_path);

egl_t* egl = &cnx->egl;

__eglMustCastToProperFunctionPointerType* curr =

(__eglMustCastToProperFunctionPointerType*)egl;

char const * const * api = egl_names;

while (*api) {

char const * name = *api;

__eglMustCastToProperFunctionPointerType f =

(__eglMustCastToProperFunctionPointerType)dlsym(dso, name);

if (f == NULL) {

// couldn't find the entry-point, use eglGetProcAddress()

f = getProcAddress(name);

if (f == NULL) {

f = (__eglMustCastToProperFunctionPointerType)0;

}

}

*curr++ = f;

api++;

}

}

if (mask & GLESv1_CM) {

ALOGD("GLESv1_CM loaded %s", driver_absolute_path);

init_api(dso, gl_names,

(__eglMustCastToProperFunctionPointerType*)

&cnx->hooks[egl_connection_t::GLESv1_INDEX]->gl,

getProcAddress);

}

if (mask & GLESv2) {

ALOGD("GLESv2 loaded %s", driver_absolute_path);

init_api(dso, gl_names,

(__eglMustCastToProperFunctionPointerType*)

&cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl,

getProcAddress);

}

return dso;

}

Loader::load_driver() 通过三个步骤完成驱动库加载:

第一步,找到驱动库文件的路径。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

class MatchFile {

public:

static String8 find(const char* kind) {

String8 result;

int emulationStatus = checkGlesEmulationStatus();

switch (emulationStatus) {

case 0:

#if defined(__LP64__)

result.setTo("/system/lib64/egl/libGLES_android.so");

#else

result.setTo("/system/lib/egl/libGLES_android.so");

#endif

return result;

case 1:

// Use host-side OpenGL through the "emulation" library

#if defined(__LP64__)

result.appendFormat("/system/lib64/egl/lib%s_emulation.so", kind);

#else

result.appendFormat("/system/lib/egl/lib%s_emulation.so", kind);

#endif

return result;

default:

// Not in emulator, or use other guest-side implementation

break;

}

String8 pattern;

pattern.appendFormat("lib%s", kind);

const char* const searchPaths[] = {

#if defined(__LP64__)

"/vendor/lib64/egl",

"/system/lib64/egl"

#else

"/vendor/lib/egl",

"/system/lib/egl"

#endif

};

// first, we search for the exact name of the GLES userspace

// driver in both locations.

// i.e.:

// libGLES.so, or:

// libEGL.so, libGLESv1_CM.so, libGLESv2.so

for (size_t i=0 ; i<NELEM(searchPaths) ; i++) {

if (find(result, pattern, searchPaths[i], true)) {

return result;

}

}

// for compatibility with the old "egl.cfg" naming convention

// we look for files that match:

// libGLES_*.so, or:

// libEGL_*.so, libGLESv1_CM_*.so, libGLESv2_*.so

pattern.append("_");

for (size_t i=0 ; i<NELEM(searchPaths) ; i++) {

if (find(result, pattern, searchPaths[i], false)) {

return result;

}

}

// we didn't find the driver. gah.

result.clear();

return result;

}

private:

static bool find(String8& result,

const String8& pattern, const char* const search, bool exact) {

if (exact) {

String8 absolutePath;

absolutePath.appendFormat("%s/%s.so", search, pattern.string());

if (!access(absolutePath.string(), R_OK)) {

result = absolutePath;

return true;

}

return false;

}

DIR* d = opendir(search);

if (d != NULL) {

struct dirent cur;

struct dirent* e;

while (readdir_r(d, &cur, &e) == 0 && e) {

if (e->d_type == DT_DIR) {

continue;

}

if (!strcmp(e->d_name, "libGLES_android.so")) {

// always skip the software renderer

continue;

}

if (strstr(e->d_name, pattern.string()) == e->d_name) {

if (!strcmp(e->d_name + strlen(e->d_name) - 3, ".so")) {

result.clear();

result.appendFormat("%s/%s", search, e->d_name);

closedir(d);

return true;

}

}

}

closedir(d);

}

return false;

}

};

String8 absolutePath = MatchFile::find(kind);

if (absolutePath.isEmpty()) {

// this happens often, we don't want to log an error

return 0;

}

checkGlesEmulationStatus() 函数,在不是运行于模拟器中时,返回 -1;在运行于模拟器中,但不支持 GPU 硬件模拟时返回 0;在运行于模拟器中,通过宿主机端的 OpenGL ES 实现来支持 GPU 硬件模拟时,返回 1;在运行于模拟器中,通过 Android 客户系统端的生产商驱动的 OpenGL ES 实现来支持 GPU 硬件模拟时,返回 2。对于运行环境的这种判断,主要依据两个系统属性,即 ro.kernel.qemu 和 qemu.gles

只有在 checkGlesEmulationStatus() 返回 0,即运行于模拟器,但不支持 OpenGL ES 的 GPU 硬件模拟时,EGL 和 OpenGL ES 库采用软件实现 /system/lib64/egl/libGLES_android.so 或 /system/lib/egl/libGLES_android.so

如果是物理设备,或者模拟器开启了 OpenGL ES 的 GPU 硬件模拟,对特定图形驱动库文件——EGL 库文件或 OpenGL ES 库文件——的查找按照如下的顺序进行:

  1. /vendor/lib64/egl 或 /vendor/lib/egl 目录下文件名符合 lib%s.so 模式,即 /vendor 下文件名完全匹配的库文件,比如 /vendor/lib64/egl/libGLES.so

  2. /system/lib64/egl 或 /system/lib/egl 目录下文件名符合 lib%s.so 模式,即 /system 下文件名完全匹配的库文件,比如 /system/lib64/egl/libGLES.so

  3. /vendor/lib64/egl 或 /vendor/lib/egl 目录下文件名符合 lib%s_*.so 模式,即 /vendor 下文件名前缀匹配的库文件,比如对于 Pixel 设备的 /vendor/lib64/egl/libEGL_adreno.so

  4. /system/lib64/egl 或 /system/lib/egl 目录下文件名符合 lib%s_*.so 模式,即 /system 下文件名前缀匹配的库文件。

Android 会优先采用 /vendor/ 下设备供应商提供的图形驱动库,优先采用库文件名完全匹配的图形驱动库。

第二步,通过 dlopen() 加载库文件。

1

2

3

4

5

6

7

8

const char* const driver_absolute_path = absolutePath.string();

void* dso = dlopen(driver_absolute_path, RTLD_NOW | RTLD_LOCAL);

if (dso == 0) {

const char* err = dlerror();

ALOGE("load_driver(%s): %s", driver_absolute_path, err?err:"unknown");

return 0;

}

第三步,初始化函数表。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

if (mask & EGL) {

ALOGD("EGL loaded %s", driver_absolute_path);

getProcAddress = (getProcAddressType)dlsym(dso, "eglGetProcAddress");

ALOGE_IF(!getProcAddress,

"can't find eglGetProcAddress() in %s", driver_absolute_path);

egl_t* egl = &cnx->egl;

__eglMustCastToProperFunctionPointerType* curr =

(__eglMustCastToProperFunctionPointerType*)egl;

char const * const * api = egl_names;

while (*api) {

char const * name = *api;

__eglMustCastToProperFunctionPointerType f =

(__eglMustCastToProperFunctionPointerType)dlsym(dso, name);

if (f == NULL) {

// couldn't find the entry-point, use eglGetProcAddress()

f = getProcAddress(name);

if (f == NULL) {

f = (__eglMustCastToProperFunctionPointerType)0;

}

}

*curr++ = f;

api++;

}

}

if (mask & GLESv1_CM) {

ALOGD("GLESv1_CM loaded %s", driver_absolute_path);

init_api(dso, gl_names,

(__eglMustCastToProperFunctionPointerType*)

&cnx->hooks[egl_connection_t::GLESv1_INDEX]->gl,

getProcAddress);

}

if (mask & GLESv2) {

ALOGD("GLESv2 loaded %s", driver_absolute_path);

init_api(dso, gl_names,

(__eglMustCastToProperFunctionPointerType*)

&cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl,

getProcAddress);

}

return dso;

}

初始化函数表主要通过 dlsym() 根据函数名,一个个找到对应的地址,并赋值给函数指针来完成。EGL 接口函数名来自于 egl_names,OpenGL ES 接口函数名来自于 gl_names,它们的定义位于 frameworks/native/opengl/libs/EGL/egl.cpp

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

#undef GL_ENTRY

#undef EGL_ENTRY

#define GL_ENTRY(_r, _api, ...) #_api,

#define EGL_ENTRY(_r, _api, ...) #_api,

char const * const gl_names[] = {

#include "../entries.in"

NULL

};

char const * const egl_names[] = {

#include "egl_entries.in"

NULL

};

#undef GL_ENTRY

#undef EGL_ENTRY

这是两个字符串数组,数组项同样来自于另外的两个文件,gl_names 的数组项来自于 egl_entries.ingl_names 的数组项来自于 entries.in,这两个文件也正是前面看到的 struct egl_t 结构和 struct gl_hooks_t 的 struct gl_t 结构的结构成员的来源。Android 通过这种方式建立函数表与函数名表中相同函数的项之间的对应关系,即对于相同的函数,它们对应的项在函数表中的偏移,与在函数名表中的偏移相同。

对于特定的进程,Android 图形驱动初始化的过程只执行一次。在 egl_init_drivers_locked() 及 Loader::load_driver() 中加日志,重新编译 frameworks/native/opengl/libs,替换设备中的 /system/lib64/libEGL.so 和 /system/lib/libEGL.so,重新启动设备,可以看到如下的日志输出:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

11-27 13:26:19.440 475 475 D libEGL : EGL loaded /vendor/lib64/egl/libEGL_adreno.so

11-27 13:26:19.628 475 475 D libEGL : GLESv1_CM loaded /vendor/lib64/egl/libGLESv1_CM_adreno.so

11-27 13:26:19.648 475 475 D libEGL : GLESv2 loaded /vendor/lib64/egl/libGLESv2_adreno.so

11-27 13:26:21.761 477 547 D libEGL : EGL loaded /vendor/lib64/egl/libEGL_adreno.so

11-27 13:26:21.776 477 547 D libEGL : GLESv1_CM loaded /vendor/lib64/egl/libGLESv1_CM_adreno.so

11-27 13:26:21.785 477 547 D libEGL : GLESv2 loaded /vendor/lib64/egl/libGLESv2_adreno.so

09-14 09:21:47.715 614 614 D libEGL : EGL loaded /vendor/lib/egl/libEGL_adreno.so

09-14 09:21:47.792 614 614 D libEGL : GLESv1_CM loaded /vendor/lib/egl/libGLESv1_CM_adreno.so

09-14 09:21:47.819 614 614 D libEGL : GLESv2 loaded /vendor/lib/egl/libGLESv2_adreno.so

09-14 09:21:48.492 612 612 D libEGL : EGL loaded /vendor/lib64/egl/libEGL_adreno.so

09-14 09:21:48.496 613 613 D libEGL : EGL loaded /vendor/lib/egl/libEGL_adreno.so

09-14 09:21:48.503 612 612 D libEGL : GLESv1_CM loaded /vendor/lib64/egl/libGLESv1_CM_adreno.so

09-14 09:21:48.510 613 613 D libEGL : GLESv1_CM loaded /vendor/lib/egl/libGLESv1_CM_adreno.so

09-14 09:21:48.513 612 612 D libEGL : GLESv2 loaded /vendor/lib64/egl/libGLESv2_adreno.so

09-14 09:21:48.523 613 613 D libEGL : GLESv2 loaded /vendor/lib/egl/libGLESv2_adreno.so

根据这些日志的进程号查找响应的进程:

1

2

3

4

system 475 1 151868 22568 SyS_epoll_ 71808e632c S /system/bin/surfaceflinger

root 612 1 2144132 81252 poll_sched 7627a2044c S zygote64

root 613 1 1578176 69960 poll_sched 00f5045700 S zygote

cameraserver 614 1 123228 45404 binder_thr 00efbf4658 S /system/bin/cameraserver

可见 Android 中需要用到 OpenGL ES 图形系统的进程主要有 surfaceflingercameraserver 和所有的 Android Java 应用程序。对于普通的 Java 应用程序,这些驱动库会先由 zygote 加载完成,后续 systemserver 指示 zygote 为普通的 Java 应用程序 fork 出新的进程,新的进程继承 zygote 加载的那些库。

图形驱动 Wrapper

============

Android 中图形驱动 Wrapper 是图形接口的一个中转。Android 中使用 EGL 和 OpenGL ES 接口的应用程序,可能是通过 framework 提供的 Java 接口,也可能通过 NDK 提供的本地层接口,直接调用图形驱动 Wrapper,这些 Wrapper 再将调用转给设备特有的 EGL 和 OpenGL ES 实现库中的函数。

通过 Android.mk 文件可以看到这样的依赖关系。frameworks/native/opengl/libs/Android.mk 文件中存在如下的内容:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

include $(CLEAR_VARS)

LOCAL_SRC_FILES:= \

EGL/egl_tls.cpp \

EGL/egl_cache.cpp \

EGL/egl_display.cpp \

EGL/egl_object.cpp \

EGL/egl.cpp \

EGL/eglApi.cpp \

EGL/getProcAddress.cpp.arm \

EGL/Loader.cpp \

#

LOCAL_SHARED_LIBRARIES += libbinder libcutils libutils liblog libui

LOCAL_MODULE:= libEGL

LOCAL_LDFLAGS += -Wl,--exclude-libs=ALL

LOCAL_SHARED_LIBRARIES += libdl

. . . . . .

include $(CLEAR_VARS)

LOCAL_SRC_FILES:= \

GLES_CM/gl.cpp.arm \

#

LOCAL_CLANG := false

LOCAL_SHARED_LIBRARIES += libcutils liblog libEGL

LOCAL_MODULE:= libGLESv1_CM

LOCAL_SHARED_LIBRARIES += libdl

. . . . . .

include $(CLEAR_VARS)

LOCAL_SRC_FILES:= \

GLES2/gl2.cpp \

#

LOCAL_CLANG := false

LOCAL_ARM_MODE := arm

LOCAL_SHARED_LIBRARIES += libcutils libutils liblog libEGL

LOCAL_MODULE:= libGLESv2

LOCAL_SHARED_LIBRARIES += libdl

. . . . . .

include $(CLEAR_VARS)

LOCAL_SRC_FILES:= \

GLES2/gl2.cpp \

#

LOCAL_CLANG := false

LOCAL_ARM_MODE := arm

LOCAL_SHARED_LIBRARIES += libcutils libutils liblog libEGL

LOCAL_MODULE:= libGLESv3

LOCAL_SHARED_LIBRARIES += libdl

. . . . . .

即 frameworks/native/opengl/libs/ 下的源码被编译为了四个动态链接库,分别为 libEGL.solibGLESv1_CMlibGLESv2 和 libGLESv3,其中 libGLESv2 和 libGLESv3 完全相同。在frameworks/base/core/jni/Android.mk 中可以清晰地看到 framework JNI 对这些库的依赖:

1

2

3

4

5

6

7

8

9

LOCAL_SHARED_LIBRARIES := \

. . . . . .

libsqlite \

libEGL \

libGLESv1_CM \

libGLESv2 \

libvulkan \

libETC1 \

. . . . . .

EGL 的 Wrapper,如我们前面看到的,位于 frameworks/native/opengl/libs/EGL,其 EGL API 实现位于 frameworks/native/opengl/libs/EGL/eglApi.cpp

GLESv1_CM 的 Wrapper 位于 frameworks/native/opengl/libs/GLES_CM,其接口实现在 gl.cpp 文件中:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

#elif defined(__arm__)

#define GET_TLS(reg) "mrc p15, 0, " #reg ", c13, c0, 3 \n"

#define API_ENTRY(_api) __attribute__((noinline)) _api

#define CALL_GL_API(_api, ...) \

asm volatile( \

GET_TLS(r12) \

"ldr r12, [r12, %[tls]] \n" \

"cmp r12, #0 \n" \

"ldrne pc, [r12, %[api]] \n" \

: \

: [tls] "J"(TLS_SLOT_OPENGL_API*4), \

[api] "J"(__builtin_offsetof(gl_hooks_t, gl._api)) \

: "r12" \

);

#elif defined(__aarch64__)

#define API_ENTRY(_api) __attribute__((noinline)) _api

#define CALL_GL_API(_api, ...) \

asm volatile( \

"mrs x16, tpidr_el0\n" \

"ldr x16, [x16, %[tls]]\n" \

"cbz x16, 1f\n" \

"ldr x16, [x16, %[api]]\n" \

"br x16\n" \

"1:\n" \

: \

: [tls] "i" (TLS_SLOT_OPENGL_API * sizeof(void*)), \

[api] "i" (__builtin_offsetof(gl_hooks_t, gl._api)) \

: "x16" \

);

#elif defined(__i386__)

. . . . . .

#define CALL_GL_API_RETURN(_api, ...) \

CALL_GL_API(_api, __VA_ARGS__) \

return 0;

extern "C" {

#pragma GCC diagnostic ignored "-Wunused-parameter"

#include "gl_api.in"

#include "glext_api.in"

#pragma GCC diagnostic warning "-Wunused-parameter"

}

#undef API_ENTRY

#undef CALL_GL_API

#undef CALL_GL_API_RETURN

这个文件包含了另外两个文件,gl_api.in 和 glext_api.in。其中 gl_api.in 文件位于 frameworks/native/opengl/libs/GLES_CM/gl_api.in,内容如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

void API_ENTRY(glAlphaFunc)(GLenum func, GLfloat ref) {

CALL_GL_API(glAlphaFunc, func, ref);

}

void API_ENTRY(glClearColor)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) {

CALL_GL_API(glClearColor, red, green, blue, alpha);

}

void API_ENTRY(glClearDepthf)(GLfloat d) {

CALL_GL_API(glClearDepthf, d);

}

void API_ENTRY(glClipPlanef)(GLenum p, const GLfloat *eqn) {

CALL_GL_API(glClipPlanef, p, eqn);

}

void API_ENTRY(glColor4f)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) {

CALL_GL_API(glColor4f, red, green, blue, alpha);

}

. . . . . .

glext_api.in 文件位于 frameworks/native/opengl/libs/GLES_CM/glext_api.in,内容如下:

1

2

3

4

5

6

7

8

9

10

11

12

void API_ENTRY(glEGLImageTargetTexture2DOES)(GLenum target, GLeglImageOES image) {

CALL_GL_API(glEGLImageTargetTexture2DOES, target, image);

}

void API_ENTRY(glEGLImageTargetRenderbufferStorageOES)(GLenum target, GLeglImageOES image) {

CALL_GL_API(glEGLImageTargetRenderbufferStorageOES, target, image);

}

void API_ENTRY(glBlendEquationSeparateOES)(GLenum modeRGB, GLenum modeAlpha) {

CALL_GL_API(glBlendEquationSeparateOES, modeRGB, modeAlpha);

}

void API_ENTRY(glBlendFuncSeparateOES)(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) {

CALL_GL_API(glBlendFuncSeparateOES, srcRGB, dstRGB, srcAlpha, dstAlpha);

}

配和前面的 API_ENTRY 和 CALL_GL_API 宏定义,可见 gl.cpp 中主要是定义了 OpenGL ES 函数接口,这些函数的实现为,借助于前面初始化的函数表,通过汇编代码,跳转到相应的 OpenGL ES 函数实现。

GLESv2 Wrapper 位于 frameworks/native/opengl/libs/GLES2,其 OpenGL ES 接口实现在 gl2.cpp 文件中:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

#elif defined(__arm__)

#define GET_TLS(reg) "mrc p15, 0, " #reg ", c13, c0, 3 \n"

#define API_ENTRY(_api) __attribute__((noinline)) _api

#define CALL_GL_API(_api, ...) \

asm volatile( \

GET_TLS(r12) \

"ldr r12, [r12, %[tls]] \n" \

"cmp r12, #0 \n" \

"ldrne pc, [r12, %[api]] \n" \

: \

: [tls] "J"(TLS_SLOT_OPENGL_API*4), \

[api] "J"(__builtin_offsetof(gl_hooks_t, gl._api)) \

: "r12" \

);

#elif defined(__aarch64__)

#define API_ENTRY(_api) __attribute__((noinline)) _api

#define CALL_GL_API(_api, ...) \

asm volatile( \

"mrs x16, tpidr_el0\n" \

"ldr x16, [x16, %[tls]]\n" \

"cbz x16, 1f\n" \

"ldr x16, [x16, %[api]]\n" \

"br x16\n" \

"1:\n" \

: \

: [tls] "i" (TLS_SLOT_OPENGL_API * sizeof(void*)), \

[api] "i" (__builtin_offsetof(gl_hooks_t, gl._api)) \

: "x16" \

);

#elif defined(__i386__)

. . . . . .

#define CALL_GL_API_RETURN(_api, ...) \

CALL_GL_API(_api, __VA_ARGS__) \

return 0;

extern "C" {

#pragma GCC diagnostic ignored "-Wunused-parameter"

#include "gl2_api.in"

#include "gl2ext_api.in"

#pragma GCC diagnostic warning "-Wunused-parameter"

}

#undef API_ENTRY

#undef CALL_GL_API

#undef CALL_GL_API_RETURN

这里的实现与前面看到的 GLESv1_CM 的 Wrapper 的接口实现类似。

Android OpenGL ES 图形库结构

=======================

Android 的 OpenGL ES 图形系统涉及多个库,根据设备类型的不同,这些库有着不同的结构。

对于模拟器,没有开启 OpenGL ES 的 GPU 硬件模拟的情况,Android OpenGL ES 图形库结构如下:

最后

希望本文对你有所启发,有任何面试上的建议也欢迎留言分享给大家。

好了,今天的分享就到这里,如果你对在面试中遇到的问题,或者刚毕业及工作几年迷茫不知道该如何准备面试并突破现状提升自己,对于自己的未来还不够了解不知道给如何规划,可以加一下下面的技术群来看看同行们都是如何突破现状,怎么学习的,来吸收他们的面试以及工作经验完善自己的之后的面试计划及职业规划。

这里放一下资料获取方式:GitHub

好了~如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。

99%E4%BA%9B%EF%BC%9F%E5%A6%82%E4%BD%95%E9%9D%A2%E8%AF%95%E6%8B%BF%E9%AB%98%E8%96%AA%EF%BC%81.md)**

[外链图片转存中…(img-4FgMwUht-1645162039952)]

好了~如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。

[外链图片转存中…(img-3S4BPRZD-1645162039954)]

为什么某些人会一直比你优秀,是因为他本身就很优秀还一直在持续努力变得更优秀,而你是不是还在满足于现状内心在窃喜!希望读到这的您能点个小赞和关注下我,以后还会更新技术干货,谢谢您的支持!

Android 图形驱动初始化(二十三),kotlin协程原理相关推荐

  1. Android 图形驱动初始化(二十三)

    从应用程序的角度看 OpenGL 图形系统的接口,主要包括两大部分,一部分是 EGL,它为 OpenGL 渲染准备环境:另一部分是 OpenGL,它执行图形渲染.通过这些接口构造渲染环境,并执行渲染的 ...

  2. Android 图形驱动初始化(二十三),移动开发者升职加薪的8项技能

    这里通过调用 EGL 库的 eglGetDisplay() 获得 Display.eglGetDisplay() 的定义位于 frameworks/native/opengl/libs/EGL/egl ...

  3. Android 图形驱动初始化

    从应用程序的角度看 OpenGL 图形系统的接口,主要包括两大部分,一部分是 EGL,它为 OpenGL 渲染准备环境:另一部分是 OpenGL,它执行图形渲染.通过这些接口构造渲染环境,并执行渲染的 ...

  4. 探索 Kotlin 协程原理

    接下来跟大家分享一下我在了解 Kotlin 协程实现的过程中理解的一些概念,如果你发现哪些地方我说错了的话,欢迎提出你的理解. 1. Kotlin 协程原理概述 Kotlin 协程的大致的执行流程如上 ...

  5. Kotlin协程:协程的基础与使用

    一.协程概述 1.概念 协程是Coroutine的中文简称,co表示协同.协作,routine表示程序.协程可以理解为多个互相协作的程序.协程是轻量级的线程,它的轻量体现在启动和切换,协程的启动不需要 ...

  6. 扔物线--Kotlin协程训练营2期-1

    笔记仅做自己学习用,方便自己复习知识.若正好可以帮助到Viewer,万分欣喜~ 若博客侵权,扔物线大大不允许放上面,麻烦告知 本文是扔物线Kotlin第二期协程训练营的第一篇文章 目录 一.Kotli ...

  7. Android中使用Kotlin协程(Coroutines)和Retrofit进行网络请求(二)之文件下载

    写在前面 下载功能是非常常用的功能,今天我们要通过kotlin协程和retrofit来是实现文件下载的功能.retorfit本身可以将请求结果以InputStream的形式返回,拿到InputStre ...

  8. 大型Android项目架构:基于组件化+模块化+Kotlin+协程+Flow+Retrofit+Jetpack+MVVM架构实现WanAndroid客户端

    前言:苟有恒,何必三更眠五更起:最无益,莫过一日曝十日寒. 前言 之前一直想写个 WanAndroid 项目来巩固自己对 Kotlin+Jetpack+协程 等知识的学习,但是一直没有时间.这里重新行 ...

  9. Android Kotlin协程和Retrofit结合使用

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/118085035 本文出自[赵彦军的博客] 往期精彩文章: Kotlin实战指南二十 ...

最新文章

  1. 北大数学天才许晨阳,回国效力6年后,为什么又去了美国任教?
  2. 朋友圈爆款背后的计算机视觉技术与应用(附视频)
  3. 那么多GAN哪个好?谷歌大脑泼来冷水:都和原版差不多
  4. elasticsearch 分页查询实现方案——Top K+归并排序
  5. 分类模型的评估方法-召回率(Recall)
  6. 广义表的学习(原理和代码)
  7. 脚本在流程中的性能影响
  8. 工作174:数组转换为对象项目案例
  9. 99%的程序员都在用Lombok,原理竟然这么简单?我也手撸了一个!
  10. (65)Vue-cli介绍
  11. java垃圾回收 分代_Java-垃圾回收机制-通用的分代垃圾回收机制
  12. bmp 和JPG有什么区别
  13. 网线分类及如何选择?
  14. 【Linux】ROS机器人操作系统的安装与使用
  15. mysql CONCAT函数 用于隐藏银行卡号码
  16. 家政服务:保姆朋友圈鄙视链在上海
  17. 2023年国家留学基金委(CSC)有关国别申请、派出注意事项
  18. 计算机ccf试题答案,【计算机本科补全计划】CCF 2017-03 试题初试
  19. Python数据分析入门--线性规划和非线性规划学习笔记
  20. 【ShardingSphere技术专题】「ShardingJDBC实战阶段」SpringBoot之整合ShardingJDBC实现分库分表(JavaConfig方式)

热门文章

  1. Cesium For UE 本地卫片导入
  2. ICS-43432MIC MEMS DIGITAL I2S OMNI -26DB
  3. node 图片加水印
  4. netif_receive_skb 函数注解
  5. 列表标签都有哪些呢?
  6. QT5中在textEdit上面显示数字、字符串
  7. tc命令——Linux基于IP进行流量限速
  8. 《2021年中国信创生态市场研究报告》发布,衡石科技入选优秀案例
  9. 菜鸟网管的入门之路-第一章、网络及硬件篇(5)(6)(7)(8)
  10. Java电子笔记(一)