1 #include "libeglvendor.h"
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #include <dlfcn.h>
8 #include <string.h>
9 #include <unistd.h>
10 #include <fnmatch.h>
11 #include <dirent.h>
12
13 #include "glvnd_pthread.h"
14 #include "libeglcurrent.h"
15 #include "libeglmapping.h"
16 #include "utils_misc.h"
17 #include "glvnd_list.h"
18 #include "cJSON.h"
19 #include "egldispatchstubs.h"
20
21 #define FILE_FORMAT_VERSION_MAJOR 1
22 #define FILE_FORMAT_VERSION_MINOR 0
23
24 static void LoadVendors(void);
25 static void TeardownVendor(__EGLvendorInfo *vendor);
26 static __EGLvendorInfo *LoadVendor(const char *filename, const char *jsonPath);
27
28 static void LoadVendorsFromConfigDir(const char *dirName);
29 static __EGLvendorInfo *LoadVendorFromConfigFile(const char *filename);
30 static cJSON *ReadJSONFile(const char *filename);
31
32 static glvnd_once_t loadVendorsOnceControl = GLVND_ONCE_INIT;
33 static struct glvnd_list __eglVendorList;
34
LoadVendors(void)35 void LoadVendors(void)
36 {
37 const char *env = NULL;
38 char **tokens;
39 int i;
40
41 // First, check to see if a list of vendors was specified.
42 if (getuid() == geteuid() && getgid() == getegid()) {
43 env = getenv("__EGL_VENDOR_LIBRARY_FILENAMES");
44 }
45 if (env != NULL) {
46 tokens = SplitString(env, NULL, ":");
47 if (tokens != NULL) {
48 for (i=0; tokens[i] != NULL; i++) {
49 LoadVendorFromConfigFile(tokens[i]);
50 }
51 free(tokens);
52 }
53 return;
54 }
55
56 // We didn't get a list of vendors, so look through the vendor config
57 // directories.
58 if (getuid() == geteuid() && getgid() == getegid()) {
59 env = getenv("__EGL_VENDOR_LIBRARY_DIRS");
60 }
61 if (env == NULL) {
62 env = DEFAULT_EGL_VENDOR_CONFIG_DIRS;
63 }
64
65 tokens = SplitString(env, NULL, ":");
66 if (tokens != NULL) {
67 for (i=0; tokens[i] != NULL; i++) {
68 LoadVendorsFromConfigDir(tokens[i]);
69 }
70 free(tokens);
71 }
72 }
73
ScandirFilter(const struct dirent * ent)74 static int ScandirFilter(const struct dirent *ent)
75 {
76 #if defined(HAVE_DIRENT_DTYPE)
77 // Ignore the entry if we know that it's not a regular file or symlink.
78 if (ent->d_type != DT_REG && ent->d_type != DT_LNK && ent->d_type != DT_UNKNOWN) {
79 return 0;
80 }
81 #endif
82
83 // Otherwise, select any JSON files.
84 if (fnmatch("*.json", ent->d_name, 0) == 0) {
85 return 1;
86 } else {
87 return 0;
88 }
89 }
90
CompareFilenames(const struct dirent ** ent1,const struct dirent ** ent2)91 static int CompareFilenames(const struct dirent **ent1, const struct dirent **ent2)
92 {
93 return strcmp((*ent1)->d_name, (*ent2)->d_name);
94 }
95
LoadVendorsFromConfigDir(const char * dirName)96 void LoadVendorsFromConfigDir(const char *dirName)
97 {
98 struct dirent **entries = NULL;
99 size_t dirnameLen;
100 const char *pathSep;
101 int count;
102 int i;
103
104 count = scandir(dirName, &entries, ScandirFilter, CompareFilenames);
105 if (count <= 0) {
106 return;
107 }
108
109 // Check if dirName ends with a "/" character. If it doesn't, then we need
110 // to add one when we construct the full file paths below.
111 dirnameLen = strlen(dirName);
112 if (dirnameLen > 0 && dirName[dirnameLen - 1] != '/') {
113 pathSep = "/";
114 } else {
115 pathSep = "";
116 }
117
118 for (i=0; i<count; i++) {
119 char *path = NULL;
120 if (glvnd_asprintf(&path, "%s%s%s", dirName, pathSep, entries[i]->d_name) > 0) {
121 LoadVendorFromConfigFile(path);
122 free(path);
123 } else {
124 fprintf(stderr, "ERROR: Could not allocate vendor library path name\n");
125 }
126 free(entries[i]);
127 }
128
129 free(entries);
130 }
131
__eglInitVendors(void)132 void __eglInitVendors(void)
133 {
134 glvnd_list_init(&__eglVendorList);
135 }
136
__eglLoadVendors(void)137 struct glvnd_list *__eglLoadVendors(void)
138 {
139 __glvndPthreadFuncs.once(&loadVendorsOnceControl, LoadVendors);
140 return &__eglVendorList;
141 }
142
__eglTeardownVendors(void)143 void __eglTeardownVendors(void)
144 {
145 __EGLvendorInfo *vendor;
146 __EGLvendorInfo *vendorTemp;
147
148 glvnd_list_for_each_entry_safe(vendor, vendorTemp, &__eglVendorList, entry) {
149 glvnd_list_del(&vendor->entry);
150 __glDispatchForceUnpatch(vendor->vendorID);
151 TeardownVendor(vendor);
152 }
153 }
154
155 const __EGLapiExports __eglExportsTable = {
156 __eglThreadInitialize, // threadInit
157 __eglQueryAPI, // getCurrentApi
158 __eglGetCurrentVendor, // getCurrentVendor
159 __eglGetCurrentContext, // getCurrentContext
160 __eglGetCurrentDisplay, // getCurrentDisplay
161 __eglGetCurrentSurface, // getCurrentSurface
162 __eglFetchDispatchEntry, // fetchDispatchEntry
163 __eglSetError, // setEGLError
164 __eglSetLastVendor, // setLastVendor
165 __eglGetVendorFromDisplay, // getVendorFromDisplay
166 __eglGetVendorFromDevice, // getVendorFromDevice
167 __eglAddDevice, // setVendorForDevice
168 };
169
TeardownVendor(__EGLvendorInfo * vendor)170 void TeardownVendor(__EGLvendorInfo *vendor)
171 {
172 if (vendor->glDispatch) {
173 __glDispatchDestroyTable(vendor->glDispatch);
174 }
175
176 /* Clean up the dynamic dispatch table */
177 if (vendor->dynDispatch != NULL) {
178 __glvndWinsysVendorDispatchDestroy(vendor->dynDispatch);
179 vendor->dynDispatch = NULL;
180 }
181
182 if (vendor->dlhandle != NULL) {
183 dlclose(vendor->dlhandle);
184 }
185
186 free(vendor);
187 }
188
LookupVendorEntrypoints(__EGLvendorInfo * vendor)189 static GLboolean LookupVendorEntrypoints(__EGLvendorInfo *vendor)
190 {
191 memset(&vendor->staticDispatch, 0, sizeof(vendor->staticDispatch));
192
193 // TODO: A lot of these should be implemented (and probably generated) as
194 // normal EGL dispatch functions, instead of having to special-case them.
195
196 #define LOADENTRYPOINT(ptr, name) do { \
197 vendor->staticDispatch.ptr = vendor->eglvc.getProcAddress(name); \
198 if (vendor->staticDispatch.ptr == NULL) { return GL_FALSE; } \
199 } while(0)
200
201 LOADENTRYPOINT(initialize, "eglInitialize" );
202 LOADENTRYPOINT(chooseConfig, "eglChooseConfig" );
203 LOADENTRYPOINT(copyBuffers, "eglCopyBuffers" );
204 LOADENTRYPOINT(createContext, "eglCreateContext" );
205 LOADENTRYPOINT(createPbufferSurface, "eglCreatePbufferSurface" );
206 LOADENTRYPOINT(createPixmapSurface, "eglCreatePixmapSurface" );
207 LOADENTRYPOINT(createWindowSurface, "eglCreateWindowSurface" );
208 LOADENTRYPOINT(destroyContext, "eglDestroyContext" );
209 LOADENTRYPOINT(destroySurface, "eglDestroySurface" );
210 LOADENTRYPOINT(getConfigAttrib, "eglGetConfigAttrib" );
211 LOADENTRYPOINT(getConfigs, "eglGetConfigs" );
212 LOADENTRYPOINT(makeCurrent, "eglMakeCurrent" );
213 LOADENTRYPOINT(queryContext, "eglQueryContext" );
214 LOADENTRYPOINT(queryString, "eglQueryString" );
215 LOADENTRYPOINT(querySurface, "eglQuerySurface" );
216 LOADENTRYPOINT(swapBuffers, "eglSwapBuffers" );
217 LOADENTRYPOINT(terminate, "eglTerminate" );
218 LOADENTRYPOINT(waitGL, "eglWaitGL" );
219 LOADENTRYPOINT(waitNative, "eglWaitNative" );
220 LOADENTRYPOINT(bindTexImage, "eglBindTexImage" );
221 LOADENTRYPOINT(releaseTexImage, "eglReleaseTexImage" );
222 LOADENTRYPOINT(surfaceAttrib, "eglSurfaceAttrib" );
223 LOADENTRYPOINT(swapInterval, "eglSwapInterval" );
224 LOADENTRYPOINT(createPbufferFromClientBuffer, "eglCreatePbufferFromClientBuffer" );
225 LOADENTRYPOINT(releaseThread, "eglReleaseThread" );
226 LOADENTRYPOINT(waitClient, "eglWaitClient" );
227 LOADENTRYPOINT(getError, "eglGetError" );
228 #undef LOADENTRYPOINT
229
230 // The remaining functions here are optional.
231 #define LOADENTRYPOINT(ptr, name) \
232 vendor->staticDispatch.ptr = vendor->eglvc.getProcAddress(name);
233
234 LOADENTRYPOINT(bindAPI, "eglBindAPI" );
235 LOADENTRYPOINT(createSync, "eglCreateSync" );
236 LOADENTRYPOINT(destroySync, "eglDestroySync" );
237 LOADENTRYPOINT(clientWaitSync, "eglClientWaitSync" );
238 LOADENTRYPOINT(getSyncAttrib, "eglGetSyncAttrib" );
239 LOADENTRYPOINT(createImage, "eglCreateImage" );
240 LOADENTRYPOINT(destroyImage, "eglDestroyImage" );
241 LOADENTRYPOINT(createPlatformWindowSurface, "eglCreatePlatformWindowSurface" );
242 LOADENTRYPOINT(createPlatformPixmapSurface, "eglCreatePlatformPixmapSurface" );
243 LOADENTRYPOINT(waitSync, "eglWaitSync" );
244 LOADENTRYPOINT(queryDevicesEXT, "eglQueryDevicesEXT" );
245
246 LOADENTRYPOINT(debugMessageControlKHR, "eglDebugMessageControlKHR" );
247 LOADENTRYPOINT(queryDebugKHR, "eglQueryDebugKHR" );
248 LOADENTRYPOINT(labelObjectKHR, "eglLabelObjectKHR" );
249 #undef LOADENTRYPOINT
250
251 // eglQueryDisplayAttrib has KHR, EXT, and NV versions. They're all
252 // interchangeable, but the vendor might not support all of them.
253 vendor->staticDispatch.queryDisplayAttrib =
254 vendor->eglvc.getProcAddress("eglQueryDisplayAttribKHR");
255 if (vendor->staticDispatch.queryDisplayAttrib == NULL) {
256 vendor->staticDispatch.queryDisplayAttrib =
257 vendor->eglvc.getProcAddress("eglQueryDisplayAttribEXT");
258 }
259 if (vendor->staticDispatch.queryDisplayAttrib == NULL) {
260 vendor->staticDispatch.queryDisplayAttrib =
261 vendor->eglvc.getProcAddress("eglQueryDisplayAttribNV");
262 }
263
264 return GL_TRUE;
265 }
266
VendorGetProcAddressCallback(const char * procName,void * param)267 static void *VendorGetProcAddressCallback(const char *procName, void *param)
268 {
269 __EGLvendorInfo *vendor = (__EGLvendorInfo *) param;
270 return vendor->eglvc.getProcAddress(procName);
271 }
272
CheckFormatVersion(const char * versionStr)273 static EGLBoolean CheckFormatVersion(const char *versionStr)
274 {
275 int major, minor, rev;
276 int len;
277
278 major = minor = rev = -1;
279 len = sscanf(versionStr, "%d.%d.%d", &major, &minor, &rev);
280 if (len < 1) {
281 return EGL_FALSE;
282 }
283 if (len < 2) {
284 minor = 0;
285 }
286 if (len < 3) {
287 rev = 0;
288 }
289 if (major != FILE_FORMAT_VERSION_MAJOR) {
290 return EGL_FALSE;
291 }
292
293 // The minor version number will be incremented if we ever add an optional
294 // value to the JSON format that libEGL has to pay attention to. That is,
295 // an older vendor library will still work, but a vendor library with a
296 // newer format than this library understands should fail.
297 if (minor > FILE_FORMAT_VERSION_MINOR) {
298 return EGL_FALSE;
299 }
300 return EGL_TRUE;
301 }
302
LoadVendorFromConfigFile(const char * filename)303 static __EGLvendorInfo *LoadVendorFromConfigFile(const char *filename)
304 {
305 __EGLvendorInfo *vendor = NULL;
306 cJSON *root;
307 cJSON *node;
308 cJSON *icdNode;
309 const char *libraryPath;
310
311 root = ReadJSONFile(filename);
312 if (root == NULL) {
313 goto done;
314 }
315
316 node = cJSON_GetObjectItem(root, "file_format_version");
317 if (node == NULL || node->type != cJSON_String) {
318 goto done;
319 }
320 if (!CheckFormatVersion(node->valuestring)) {
321 goto done;
322 }
323
324 icdNode = cJSON_GetObjectItem(root, "ICD");
325 if (icdNode == NULL || icdNode->type != cJSON_Object) {
326 goto done;
327 }
328
329 node = cJSON_GetObjectItem(icdNode, "library_path");
330 if (node == NULL || node->type != cJSON_String) {
331 goto done;
332 }
333 libraryPath = node->valuestring;
334 vendor = LoadVendor(libraryPath, filename);
335
336 done:
337 if (root != NULL) {
338 cJSON_Delete(root);
339 }
340 if (vendor != NULL) {
341 glvnd_list_append(&vendor->entry, &__eglVendorList);
342 }
343 return vendor;
344 }
345
ReadJSONFile(const char * filename)346 static cJSON *ReadJSONFile(const char *filename)
347 {
348 FILE *in = NULL;
349 char *buf = NULL;
350 cJSON *root = NULL;
351 struct stat st;
352
353 in = fopen(filename, "r");
354 if (in == NULL) {
355 goto done;
356 }
357
358 if (fstat(fileno(in), &st) != 0) {
359 goto done;
360 }
361
362 buf = (char *) malloc(st.st_size + 1);
363 if (buf == NULL) {
364 goto done;
365 }
366
367 if (fread(buf, st.st_size, 1, in) != 1) {
368 goto done;
369 }
370 buf[st.st_size] = '\0';
371
372 root = cJSON_Parse(buf);
373
374 done:
375 if (in != NULL) {
376 fclose(in);
377 }
378 if (buf != NULL) {
379 free(buf);
380 }
381 return root;
382 }
383
CheckVendorExtensionString(__EGLvendorInfo * vendor,const char * str)384 static void CheckVendorExtensionString(__EGLvendorInfo *vendor, const char *str)
385 {
386 static const char NAME_DEVICE_BASE[] = "EGL_EXT_device_base";
387 static const char NAME_DEVICE_ENUM[] = "EGL_EXT_device_enumeration";
388 static const char NAME_PLATFORM_DEVICE[] = "EGL_EXT_platform_device";
389 static const char NAME_MESA_PLATFORM_GBM[] = "EGL_MESA_platform_gbm";
390 static const char NAME_KHR_PLATFORM_GBM[] = "EGL_KHR_platform_gbm";
391 static const char NAME_EXT_PLATFORM_WAYLAND[] = "EGL_EXT_platform_wayland";
392 static const char NAME_KHR_PLATFORM_WAYLAND[] = "EGL_KHR_platform_wayland";
393 static const char NAME_EXT_PLATFORM_X11[] = "EGL_EXT_platform_x11";
394 static const char NAME_KHR_PLATFORM_X11[] = "EGL_KHR_platform_x11";
395
396 if (str == NULL || str[0] == '\x00') {
397 return;
398 }
399
400 if (!vendor->supportsDevice) {
401 if (IsTokenInString(str, NAME_DEVICE_BASE, sizeof(NAME_DEVICE_BASE) - 1, " ")
402 || IsTokenInString(str, NAME_DEVICE_ENUM, sizeof(NAME_DEVICE_ENUM) - 1, " ")) {
403 vendor->supportsDevice = EGL_TRUE;
404 }
405 }
406
407 if (!vendor->supportsPlatformDevice) {
408 if (IsTokenInString(str, NAME_PLATFORM_DEVICE, sizeof(NAME_PLATFORM_DEVICE) - 1, " ")) {
409 vendor->supportsPlatformDevice = EGL_TRUE;
410 }
411 }
412
413 if (!vendor->supportsPlatformGbm) {
414 if (IsTokenInString(str, NAME_MESA_PLATFORM_GBM, sizeof(NAME_MESA_PLATFORM_GBM) - 1, " ")
415 || IsTokenInString(str, NAME_KHR_PLATFORM_GBM, sizeof(NAME_KHR_PLATFORM_GBM) - 1, " ")) {
416 vendor->supportsPlatformGbm = EGL_TRUE;
417 }
418 }
419
420 if (!vendor->supportsPlatformWayland) {
421 if (IsTokenInString(str, NAME_EXT_PLATFORM_WAYLAND, sizeof(NAME_EXT_PLATFORM_WAYLAND) - 1, " ")
422 || IsTokenInString(str, NAME_KHR_PLATFORM_WAYLAND, sizeof(NAME_KHR_PLATFORM_WAYLAND) - 1, " ")) {
423 vendor->supportsPlatformWayland = EGL_TRUE;
424 }
425 }
426
427 if (!vendor->supportsPlatformX11) {
428 if (IsTokenInString(str, NAME_EXT_PLATFORM_X11, sizeof(NAME_EXT_PLATFORM_X11) - 1, " ")
429 || IsTokenInString(str, NAME_KHR_PLATFORM_X11, sizeof(NAME_KHR_PLATFORM_X11) - 1, " ")) {
430 vendor->supportsPlatformX11 = EGL_TRUE;
431 }
432 }
433 }
434
CheckVendorExtensions(__EGLvendorInfo * vendor)435 static void CheckVendorExtensions(__EGLvendorInfo *vendor)
436 {
437 CheckVendorExtensionString(vendor,
438 vendor->staticDispatch.queryString(EGL_NO_DISPLAY, EGL_EXTENSIONS));
439
440 if (vendor->eglvc.getVendorString != NULL) {
441 CheckVendorExtensionString(vendor,
442 vendor->eglvc.getVendorString(__EGL_VENDOR_STRING_PLATFORM_EXTENSIONS));
443 }
444
445 if (vendor->staticDispatch.queryDevicesEXT == NULL) {
446 vendor->supportsDevice = EGL_FALSE;
447 }
448
449 if (!vendor->supportsDevice) {
450 vendor->supportsPlatformDevice = EGL_FALSE;
451 }
452 }
453
LoadVendor(const char * filename,const char * jsonPath)454 static __EGLvendorInfo *LoadVendor(const char *filename, const char *jsonPath)
455 {
456 char *absolutePath = NULL;
457 char *jsonDir = NULL;
458 const char *dlopenName;
459 __PFNEGLMAINPROC eglMainProc;
460 __EGLvendorInfo *vendor = NULL;
461 __EGLvendorInfo *otherVendor;
462 int i;
463
464 // Allocate the vendor structure, plus enough room for a copy of its name.
465 vendor = (__EGLvendorInfo *) calloc(1, sizeof(__EGLvendorInfo));
466 if (vendor == NULL) {
467 return NULL;
468 }
469
470 if (filename == NULL) {
471 // Clearly not going to work
472 goto fail;
473 }
474 else if (filename[0] == '/') {
475 // filename is an absolute path, no special handling needed
476 // e.g. /usr/lib/libEGL_myvendor.so.0
477 dlopenName = filename;
478 }
479 else if (strchr(filename, '/') == NULL) {
480 // filename is a bare SONAME, no special handling needed
481 // e.g. libEGL_myvendor.so.0
482 dlopenName = filename;
483 }
484 else {
485 char *slash;
486
487 // filename is a relative path; we have to interpret it as
488 // relative to *somewhere*. dlopen() would interpret it as relative
489 // to the current working directory, but that seems unlikely to be
490 // useful. Instead, follow Vulkan by interpreting it as relative
491 // to the directory where we found the ICD.
492 // e.g. it might be ../../../$LIB/libEGL_myvendor.so.0
493
494 // Resolve symlinks and relative components in jsonPath. This assumes
495 // a POSIX.1-2008-compliant realpath(), similar to the implementations
496 // in glibc and musl.
497 jsonDir = realpath(jsonPath, NULL);
498 if (jsonDir == NULL) {
499 goto fail;
500 }
501
502 // Truncate jsonDir at the last slash to get the directory,
503 // e.g. /usr/share/glvnd/egl_vendor.d
504 slash = strrchr(jsonDir, '/');
505 if (slash == NULL) {
506 // Shouldn't happen, because the output of realpath() is absolute;
507 // recover by just not loading it
508 goto fail;
509 }
510 *slash = '\0';
511
512 // Concatenate jsonDir and filename
513 // e.g. /usr/share/glvnd/egl_vendor.d/../../../$LIB/libEGL_myvendor.so.0
514 if (glvnd_asprintf(&absolutePath, "%s/%s", jsonDir, filename) < 0) {
515 goto fail;
516 }
517
518 dlopenName = absolutePath;
519 }
520
521 vendor->dlhandle = dlopen(dlopenName, RTLD_LAZY);
522 if (vendor->dlhandle == NULL) {
523 goto fail;
524 }
525
526 // Check if this vendor was already loaded under a different name.
527 glvnd_list_for_each_entry(otherVendor, &__eglVendorList, entry) {
528 if (otherVendor->dlhandle == vendor->dlhandle) {
529 goto fail;
530 }
531 }
532
533 eglMainProc = dlsym(vendor->dlhandle, __EGL_MAIN_PROTO_NAME);
534 if (!eglMainProc) {
535 goto fail;
536 }
537
538 if (!(*eglMainProc)(EGL_VENDOR_ABI_VERSION,
539 &__eglExportsTable,
540 vendor, &vendor->eglvc)) {
541 goto fail;
542 }
543
544 // Make sure all the required functions are there.
545 if (vendor->eglvc.getPlatformDisplay == NULL
546 || vendor->eglvc.getSupportsAPI == NULL
547 || vendor->eglvc.getProcAddress == NULL
548 || vendor->eglvc.getDispatchAddress == NULL
549 || vendor->eglvc.setDispatchIndex == NULL) {
550 goto fail;
551 }
552
553 if (vendor->eglvc.isPatchSupported != NULL
554 && vendor->eglvc.initiatePatch != NULL) {
555 vendor->patchCallbacks.isPatchSupported = vendor->eglvc.isPatchSupported;
556 vendor->patchCallbacks.initiatePatch = vendor->eglvc.initiatePatch;
557 vendor->patchCallbacks.releasePatch = vendor->eglvc.releasePatch;
558 vendor->patchCallbacks.threadAttach = vendor->eglvc.patchThreadAttach;
559 vendor->patchSupported = EGL_TRUE;
560 }
561
562 if (!LookupVendorEntrypoints(vendor)) {
563 goto fail;
564 }
565
566 vendor->supportsGL = vendor->eglvc.getSupportsAPI(EGL_OPENGL_API);
567 vendor->supportsGLES = vendor->eglvc.getSupportsAPI(EGL_OPENGL_ES_API);
568 if (!(vendor->supportsGL || vendor->supportsGLES)) {
569 goto fail;
570 }
571
572 vendor->vendorID = __glDispatchNewVendorID();
573 assert(vendor->vendorID >= 0);
574
575 // TODO: Allow per-context dispatch tables?
576 vendor->glDispatch = __glDispatchCreateTable(VendorGetProcAddressCallback, vendor);
577 if (!vendor->glDispatch) {
578 goto fail;
579 }
580
581 CheckVendorExtensions(vendor);
582
583 // Create and initialize the EGL dispatch table.
584 // This is called before trying to look up any vendor-supplied EGL dispatch
585 // functions, so we only need to add the EGL dispatch functions that are
586 // defined in libEGL itself.
587 vendor->dynDispatch = __glvndWinsysVendorDispatchCreate();
588 if (!vendor->dynDispatch) {
589 goto fail;
590 }
591 for (i=0; i<__EGL_DISPATCH_FUNC_COUNT; i++) {
592 vendor->eglvc.setDispatchIndex(
593 __EGL_DISPATCH_FUNC_NAMES[i],
594 __EGL_DISPATCH_FUNC_INDICES[i]);
595 }
596
597 free(absolutePath);
598 free(jsonDir);
599 return vendor;
600
601 fail:
602 if (vendor != NULL) {
603 TeardownVendor(vendor);
604 }
605 free(absolutePath);
606 free(jsonDir);
607 return NULL;
608 }
609
610