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