2024年安卓最全Android 图形驱动初始化(二十三),阿里p6三面

最后

其实要轻松掌握很简单,要点就两个:

  1. 找到一套好的视频资料,紧跟大牛梳理好的知识框架进行学习。
  2. 多练。 (视频优势是互动感强,容易集中注意力)

你不需要是天才,也不需要具备强悍的天赋,只要做到这两点,短期内成功的概率是非常高的。

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。

阿里P7Android高级教程

下面资料部分截图,诚意满满:特别适合有3-5年开发经验的Android程序员们学习。

附送高清脑图,高清知识点讲解教程,以及一些面试真题及答案解析。送给需要的提升技术、近期面试跳槽、自身职业规划迷茫的朋友们。

Android核心高级技术PDF资料,BAT大厂面试真题解析;

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

然后来看 eglGetDisplay()EGLImpl 的定义位于 frameworks/base/opengl/java/com/google/android/gles_jni/EGLImpl.java,其中 eglGetDisplay() 定义如下:

1

2

3

4

5

6

7

8

9

10

11

public synchronized EGLDisplay eglGetDisplay(Object native_display) {

long value = _eglGetDisplay(native_display);

if (value == 0) {

return EGL10.EGL_NO_DISPLAY;

}

if (mDisplay.mEGLDisplay != value)

mDisplay = new EGLDisplayImpl(value);

return mDisplay;

}

. . . . . .

private native long _eglGetDisplay(Object native_display);

这里主要通过调用本地层方法 _eglGetDisplay() 得到 Display,然后创建 EGLDisplayImpl_eglGetDisplay() 返回底层的 Display 对象句柄。

EGLDisplayImpl 的实现很简单:

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

package com.google.android.gles_jni;

import javax.microedition.khronos.egl.*;

public class EGLDisplayImpl extends EGLDisplay {

long mEGLDisplay;

public EGLDisplayImpl(long dpy) {

mEGLDisplay = dpy;

}

@Override

public boolean equals(Object o) {

if (this == o) return true;

if (o == null || getClass() != o.getClass()) return false;

EGLDisplayImpl that = (EGLDisplayImpl) o;

return mEGLDisplay == that.mEGLDisplay;

}

@Override

public int hashCode() {

/*

* Based on the algorithm suggested in

* http://developer.android.com/reference/java/lang/Object.html

*/

int result = 17;

result = 31 * result + (int) (mEGLDisplay ^ (mEGLDisplay >>> 32));

return result;

}

}

EGLDispaly 实际是一个标记接口:

1

2

3

4

5

package javax.microedition.khronos.egl;

public abstract class EGLDisplay

{

}

可见 EGLDisplayImpl 仅仅包装了本地层返回的 Display 对象句柄。

本地层 _eglGetDisplay() 的实现位于 frameworks/base/core/jni/com_google_android_gles_jni_EGLImpl.cpp

1

2

3

4

5

static jlong jni_eglGetDisplay(JNIEnv *_env, jobject _this, jobject native_display) {

return reinterpret_cast<jlong>(eglGetDisplay(EGL_DEFAULT_DISPLAY));

}

. . . . . .

{"_eglGetDisplay", "(" OBJECT ")J", (void*)jni_eglGetDisplay },

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

EGLDisplay eglGetDisplay(EGLNativeDisplayType display)

{

clearError();

uintptr_t index = reinterpret_cast<uintptr_t>(display);

if (index >= NUM_DISPLAYS) {

return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY);

}

if (egl_init_drivers() == EGL_FALSE) {

return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY);

}

EGLDisplay dpy = egl_display_t::getFromNativeDisplay(display);

return dpy;

}

在这个函数中,首先根据需要执行 egl_init_drivers() 初始化驱动库。然后通过 egl_display_t::getFromNativeDisplay(display) 获得 Dispaly。

egl_display_t::getFromNativeDisplay(display) 的定义(位于 frameworks/native/opengl/libs/EGL/egl_display.cpp)如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

EGLDisplay egl_display_t::getDisplay(EGLNativeDisplayType display) {

Mutex::Autolock _l(lock);

// get our driver loader

Loader& loader(Loader::getInstance());

egl_connection_t* const cnx = &gEGLImpl;

if (cnx->dso && disp.dpy == EGL_NO_DISPLAY) {

EGLDisplay dpy = cnx->egl.eglGetDisplay(display);

disp.dpy = dpy;

if (dpy == EGL_NO_DISPLAY) {

loader.close(cnx->dso);

cnx->dso = NULL;

}

}

return EGLDisplay(uintptr_t(display) + 1U);

}

在这个函数中,最为关键的就是,在 disp.dpy 为 EGL_NO_DISPLAY 时,通过 cnx->egl.eglGetDisplay() 初始化它了。

EGLDisplay 的定义如下:

1

typedef void *EGLDisplay;

它仅是 void 指针的 typedef。

总结一下获取 Display 的整个过程

  • 通过 frameworks/native/opengl/libs/EGL 初始化图形驱动;

  • 通过厂商提供的设备特有的 EGL 库接口初始化 Display。

Android 图形驱动初始化

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

接下来更详细地看一下图形驱动初始化。这通过 egl_init_drivers() 完成,该函数定义 (位于frameworks/native/opengl/libs/EGL/egl.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

egl_connection_t gEGLImpl;

gl_hooks_t gHooks[2];

. . . . . .

static EGLBoolean egl_init_drivers_locked() {

if (sEarlyInitState) {

// initialized by static ctor. should be set here.

return EGL_FALSE;

}

// get our driver loader

Loader& loader(Loader::getInstance());

// dynamically load our EGL implementation

egl_connection_t* cnx = &gEGLImpl;

if (cnx->dso == 0) {

cnx->hooks[egl_connection_t::GLESv1_INDEX] =

&gHooks[egl_connection_t::GLESv1_INDEX];

cnx->hooks[egl_connection_t::GLESv2_INDEX] =

&gHooks[egl_connection_t::GLESv2_INDEX];

cnx->dso = loader.open(cnx);

}

return cnx->dso ? EGL_TRUE : EGL_FALSE;

}

static pthread_mutex_t sInitDriverMutex = PTHREAD_MUTEX_INITIALIZER;

EGLBoolean egl_init_drivers() {

EGLBoolean res;

pthread_mutex_lock(&sInitDriverMutex);

res = egl_init_drivers_locked();

pthread_mutex_unlock(&sInitDriverMutex);

return res;

}

图形驱动初始化通过 Loader::open(egl_connection_t* cnx) 完成,初始化的结果将存储于全局结构 egl_connection_t gEGLImpl 和 gl_hooks_t gHooks[2] 中。

来看一下 gl_hooks_t 的定义(位于 frameworks/native/opengl/libs/hooks.h):

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

// maximum number of GL extensions that can be used simultaneously in

// a given process. this limitation exists because we need to have

// a static function for each extension and currently these static functions

// are generated at compile time.

#define MAX_NUMBER_OF_GL_EXTENSIONS 256

. . . . . .

#undef GL_ENTRY

#undef EGL_ENTRY

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

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

struct egl_t {

#include "EGL/egl_entries.in"

};

struct gl_hooks_t {

struct gl_t {

#include "entries.in"

} gl;

struct gl_ext_t {

__eglMustCastToProperFunctionPointerType extensions[MAX_NUMBER_OF_GL_EXTENSIONS];

} ext;

};

#undef GL_ENTRY

#undef EGL_ENTRY

其中 __eglMustCastToProperFunctionPointerType 定义 (位于frameworks/native/opengl/include/EGL/egl.h) 如下:

1

2

3

4

/* This is a generic function pointer type, whose name indicates it must

* be cast to the proper type *and calling convention* before use.

*/

typedef void (*__eglMustCastToProperFunctionPointerType)(void);

__eglMustCastToProperFunctionPointerType 是函数指针类型。struct gl_hooks_t 的 struct gl_ext_t ext 即为函数指针表,它们用来描述 EGL 扩展接口。

struct gl_hooks_t 内的 struct gl_t 结构体,其结构体成员在另外一个文件,即 entries.in 中定义,该文件位于 frameworks/native/opengl/libs/entries.in

1

2

3

4

5

6

GL_ENTRY(void, glActiveShaderProgram, GLuint pipeline, GLuint program)

GL_ENTRY(void, glActiveShaderProgramEXT, GLuint pipeline, GLuint program)

GL_ENTRY(void, glActiveTexture, GLenum texture)

GL_ENTRY(void, glAlphaFunc, GLenum func, GLfloat ref)

GL_ENTRY(void, glAlphaFuncQCOM, GLenum func, GLclampf ref)

. . . . . .

配合 frameworks/native/opengl/libs/hooks.h 中 GL_ENTRY 宏的定义:

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架构学习进阶是一条漫长而艰苦的道路,不能靠一时激情,更不是熬几天几夜就能学好的,必须养成平时努力学习的习惯。所以:贵在坚持!

上面分享的字节跳动公司2020年的面试真题解析大全,笔者还把一线互联网企业主流面试技术要点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

就先写到这,码字不易,写的很片面不好之处敬请指出,如果觉得有参考价值的朋友也可以关注一下我

①「Android面试真题解析大全」PDF完整高清版+②「Android面试知识体系」学习思维导图压缩包阅读下载,最后觉得有帮助、有需要的朋友可以点个赞

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

731137719)]

[外链图片转存中…(img-j7WskJVu-1715731137720)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 17
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值