1 // Copyright 2014 Emil Velikov
2 //
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are met:
7 //
8 // - Redistributions of source code must retain the above copyright notice, this
9 // list of conditions and the following disclaimer.
10 //
11 // - Redistributions in binary form must reproduce the above copyright notice,
12 // this list of conditions and the following disclaimer in the documentation
13 // and/or other materials provided with the distribution.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
26 #define _POSIX_C_SOURCE 200112 // glib feature macro for unsetenv()
27
28 #include <dlfcn.h>
29
30 #include "wcore_error.h"
31 #include "wegl_platform.h"
32
33
34 #ifdef WAFFLE_HAS_ANDROID
35 static const char *libEGL_filename = "libEGL.so";
36 #else
37 static const char *libEGL_filename = "libEGL.so.1";
38 #endif
39
40 static void
setup_env(const struct wegl_platform * self)41 setup_env(const struct wegl_platform *self)
42 {
43 switch (self->egl_platform) {
44 case EGL_PLATFORM_ANDROID_KHR:
45 // Don't set EGL_PLATFORM because I don't know the impact doing so
46 // on Android. Does anything other than Mesa use it?
47 break;
48 case EGL_PLATFORM_GBM_KHR:
49 setenv("EGL_PLATFORM", "drm", true);
50 break;
51 case EGL_PLATFORM_WAYLAND_KHR:
52 setenv("EGL_PLATFORM", "wayland", true);
53 break;
54 case EGL_PLATFORM_X11_KHR:
55 setenv("EGL_PLATFORM", "x11", true);
56 break;
57 case EGL_PLATFORM_SURFACELESS_MESA:
58 setenv("EGL_PLATFORM", "surfaceless", true);
59 break;
60 default:
61 assert(!"bad egl_platform enum");
62 break;
63 }
64 }
65
66 bool
wegl_platform_teardown(struct wegl_platform * self)67 wegl_platform_teardown(struct wegl_platform *self)
68 {
69 bool ok = true;
70 int error = 0;
71
72 if (!wegl_platform_can_use_eglGetPlatformDisplay(self) &&
73 !wegl_platform_can_use_eglGetPlatformDisplayEXT(self) &&
74 self->egl_platform != EGL_PLATFORM_ANDROID_KHR) {
75 unsetenv("EGL_PLATFORM");
76 }
77
78 if (self->eglHandle) {
79 error = dlclose(self->eglHandle);
80 if (error) {
81 ok = false;
82 wcore_errorf(WAFFLE_ERROR_UNKNOWN,
83 "dlclose(\"%s\") failed: %s",
84 libEGL_filename, dlerror());
85 }
86 }
87
88 ok &= wcore_platform_teardown(&self->wcore);
89 return ok;
90 }
91
92 bool
wegl_platform_init(struct wegl_platform * self,EGLenum egl_platform)93 wegl_platform_init(struct wegl_platform *self, EGLenum egl_platform)
94 {
95 bool ok;
96
97 ok = wcore_platform_init(&self->wcore);
98 if (!ok)
99 goto error;
100
101 self->egl_platform = egl_platform;
102
103 // Most Waffle platforms will call eglCreateWindowSurface.
104 self->egl_surface_type_mask = EGL_WINDOW_BIT;
105
106 self->eglHandle = dlopen(libEGL_filename, RTLD_LAZY | RTLD_LOCAL);
107 if (!self->eglHandle) {
108 wcore_errorf(WAFFLE_ERROR_FATAL,
109 "dlopen(\"%s\") failed: %s",
110 libEGL_filename, dlerror());
111 ok = false;
112 goto error;
113 }
114
115 #define RETRIEVE_EGL_SYMBOL(function) \
116 self->function = dlsym(self->eglHandle, #function); \
117 if (!self->function) { \
118 wcore_errorf(WAFFLE_ERROR_FATAL, \
119 "dlsym(\"%s\", \"" #function "\") failed: %s", \
120 libEGL_filename, dlerror()); \
121 ok = false; \
122 goto error; \
123 }
124
125 // Use eglGetProcAddress to get EGL 1.5 symbols, not dlsym, because the
126 // EGL 1.5 spec requires that implementors support eglGetProcAddress for
127 // all symbols.
128 //
129 // From the EGL 1.5 spec:
130 //
131 // eglGetProcAddress may be queried for all EGL and client API functions
132 // supported by the implementation (whether those functions are
133 // extensions or not, and whether they are supported by the current
134 // client API context or not).
135 //
136 // For functions that are queryable with eglGetProcAddress,
137 // implementations may choose to also export those functions statically
138 // from the object libraries implementing those functions. However,
139 // portable clients cannot rely on this behavior.
140 //
141 #define RETRIEVE_EGL_SYMBOL_OPTIONAL(function) \
142 self->function = (void*) self->eglGetProcAddress(#function);
143
144 RETRIEVE_EGL_SYMBOL(eglMakeCurrent);
145 RETRIEVE_EGL_SYMBOL(eglGetProcAddress);
146
147 // display
148 RETRIEVE_EGL_SYMBOL(eglGetDisplay);
149 RETRIEVE_EGL_SYMBOL(eglInitialize);
150 RETRIEVE_EGL_SYMBOL(eglQueryString);
151 RETRIEVE_EGL_SYMBOL(eglGetError);
152 RETRIEVE_EGL_SYMBOL(eglTerminate);
153
154 // config
155 RETRIEVE_EGL_SYMBOL(eglChooseConfig);
156
157 // context
158 RETRIEVE_EGL_SYMBOL(eglBindAPI);
159 RETRIEVE_EGL_SYMBOL(eglCreateContext);
160 RETRIEVE_EGL_SYMBOL(eglDestroyContext);
161
162 // window
163 RETRIEVE_EGL_SYMBOL(eglGetConfigAttrib);
164 RETRIEVE_EGL_SYMBOL(eglCreateWindowSurface);
165 RETRIEVE_EGL_SYMBOL(eglCreatePbufferSurface);
166 RETRIEVE_EGL_SYMBOL(eglDestroySurface);
167 RETRIEVE_EGL_SYMBOL(eglSwapBuffers);
168
169 // EGL 1.5
170 RETRIEVE_EGL_SYMBOL_OPTIONAL(eglGetPlatformDisplay);
171
172 // EGL_EXT_platform_display
173 RETRIEVE_EGL_SYMBOL_OPTIONAL(eglGetPlatformDisplayEXT);
174
175 // EGL_EXT_image_dma_buf_import_modifiers
176 RETRIEVE_EGL_SYMBOL_OPTIONAL(eglQueryDmaBufFormatsEXT);
177 RETRIEVE_EGL_SYMBOL_OPTIONAL(eglQueryDmaBufModifiersEXT);
178
179 #undef RETRIEVE_EGL_SYMBOL
180 #undef RETRIEVE_EGL_SYMBOL_OPTIONAL
181
182 self->client_extensions =
183 self->eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
184
185 if (!wegl_platform_can_use_eglGetPlatformDisplay(self) &&
186 !wegl_platform_can_use_eglGetPlatformDisplayEXT(self)) {
187 setup_env(self);
188 }
189
190 error:
191 // On failure the caller of wegl_platform_init will trigger it's own
192 // destruction which will execute wegl_platform_teardown.
193 return ok;
194 }
195
196 bool
wegl_platform_can_use_eglGetPlatformDisplay(const struct wegl_platform * plat)197 wegl_platform_can_use_eglGetPlatformDisplay(const struct wegl_platform *plat)
198 {
199 const char *ext;
200
201 if (!plat->eglGetPlatformDisplay)
202 return false;
203
204 switch (plat->egl_platform) {
205 case EGL_PLATFORM_ANDROID_KHR:
206 ext = "EGL_KHR_platform_android";
207 break;
208 case EGL_PLATFORM_GBM_KHR:
209 ext = "EGL_KHR_platform_gbm";
210 break;
211 case EGL_PLATFORM_WAYLAND_KHR:
212 ext = "EGL_KHR_platform_wayland";
213 break;
214 case EGL_PLATFORM_X11_KHR:
215 ext = "EGL_KHR_platform_x11";
216 break;
217 case EGL_PLATFORM_SURFACELESS_MESA:
218 ext = "EGL_MESA_platform_surfaceless";
219 break;
220 default:
221 assert(!"bad egl_platform enum");
222 return false;
223 }
224
225 return waffle_is_extension_in_string(plat->client_extensions, ext);
226 }
227
228 bool
wegl_platform_can_use_eglGetPlatformDisplayEXT(const struct wegl_platform * plat)229 wegl_platform_can_use_eglGetPlatformDisplayEXT(const struct wegl_platform *plat)
230 {
231 const char *ext;
232
233 if (!plat->eglGetPlatformDisplayEXT)
234 return false;
235
236 switch (plat->egl_platform) {
237 case EGL_PLATFORM_ANDROID_KHR:
238 // There exist no Android extension for eglGetPlatformDisplayEXT.
239 return false;
240 case EGL_PLATFORM_GBM_KHR:
241 ext = "EGL_MESA_platform_gbm";
242 break;
243 case EGL_PLATFORM_WAYLAND_KHR:
244 ext = "EGL_EXT_platform_wayland";
245 break;
246 case EGL_PLATFORM_X11_KHR:
247 ext = "EGL_EXT_platform_x11";
248 break;
249 case EGL_PLATFORM_SURFACELESS_MESA:
250 ext = "EGL_MESA_platform_surfaceless";
251 break;
252 default:
253 assert(!"bad egl_platform enum");
254 return false;
255 }
256
257 return waffle_is_extension_in_string(plat->client_extensions, ext);
258 }
259