xref: /openbmc/qemu/ui/egl-helpers.c (revision b23197f9)
1 #include "qemu/osdep.h"
2 #include <glob.h>
3 #include <dirent.h>
4 
5 #include "config-host.h"
6 #include "ui/egl-helpers.h"
7 
8 EGLDisplay *qemu_egl_display;
9 EGLConfig qemu_egl_config;
10 
11 /* ---------------------------------------------------------------------- */
12 
13 static bool egl_gles;
14 static int egl_debug;
15 
16 #define egl_dbg(_x ...)                          \
17     do {                                         \
18         if (egl_debug) {                         \
19             fprintf(stderr, "egl: " _x);         \
20         }                                        \
21     } while (0);
22 
23 /* ---------------------------------------------------------------------- */
24 
25 #ifdef CONFIG_OPENGL_DMABUF
26 
27 int qemu_egl_rn_fd;
28 struct gbm_device *qemu_egl_rn_gbm_dev;
29 EGLContext qemu_egl_rn_ctx;
30 
31 int qemu_egl_rendernode_open(void)
32 {
33     DIR *dir;
34     struct dirent *e;
35     int r, fd;
36     char *p;
37 
38     dir = opendir("/dev/dri");
39     if (!dir) {
40         return -1;
41     }
42 
43     fd = -1;
44     while ((e = readdir(dir))) {
45         if (e->d_type != DT_CHR) {
46             continue;
47         }
48 
49         if (strncmp(e->d_name, "renderD", 7)) {
50             continue;
51         }
52 
53         r = asprintf(&p, "/dev/dri/%s", e->d_name);
54         if (r < 0) {
55             return -1;
56         }
57 
58         r = open(p, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK);
59         if (r < 0) {
60             free(p);
61             continue;
62         }
63         fd = r;
64         free(p);
65         break;
66     }
67 
68     closedir(dir);
69     if (fd < 0) {
70         return -1;
71     }
72     return fd;
73 }
74 
75 int egl_rendernode_init(void)
76 {
77     qemu_egl_rn_fd = -1;
78 
79     qemu_egl_rn_fd = qemu_egl_rendernode_open();
80     if (qemu_egl_rn_fd == -1) {
81         fprintf(stderr, "egl: no drm render node available\n");
82         goto err;
83     }
84 
85     qemu_egl_rn_gbm_dev = gbm_create_device(qemu_egl_rn_fd);
86     if (!qemu_egl_rn_gbm_dev) {
87         fprintf(stderr, "egl: gbm_create_device failed\n");
88         goto err;
89     }
90 
91     qemu_egl_init_dpy((EGLNativeDisplayType)qemu_egl_rn_gbm_dev, false, false);
92 
93     if (!epoxy_has_egl_extension(qemu_egl_display,
94                                  "EGL_KHR_surfaceless_context")) {
95         fprintf(stderr, "egl: EGL_KHR_surfaceless_context not supported\n");
96         goto err;
97     }
98     if (!epoxy_has_egl_extension(qemu_egl_display,
99                                  "EGL_MESA_image_dma_buf_export")) {
100         fprintf(stderr, "egl: EGL_MESA_image_dma_buf_export not supported\n");
101         goto err;
102     }
103 
104     qemu_egl_rn_ctx = qemu_egl_init_ctx();
105     if (!qemu_egl_rn_ctx) {
106         fprintf(stderr, "egl: egl_init_ctx failed\n");
107         goto err;
108     }
109 
110     return 0;
111 
112 err:
113     if (qemu_egl_rn_gbm_dev) {
114         gbm_device_destroy(qemu_egl_rn_gbm_dev);
115     }
116     if (qemu_egl_rn_fd != -1) {
117         close(qemu_egl_rn_fd);
118     }
119 
120     return -1;
121 }
122 
123 int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, EGLint *fourcc)
124 {
125     EGLImageKHR image;
126     EGLint num_planes, fd;
127 
128     image = eglCreateImageKHR(qemu_egl_display, eglGetCurrentContext(),
129                               EGL_GL_TEXTURE_2D_KHR,
130                               (EGLClientBuffer)(unsigned long)tex_id,
131                               NULL);
132     if (!image) {
133         return -1;
134     }
135 
136     eglExportDMABUFImageQueryMESA(qemu_egl_display, image, fourcc,
137                                   &num_planes, NULL);
138     if (num_planes != 1) {
139         eglDestroyImageKHR(qemu_egl_display, image);
140         return -1;
141     }
142     eglExportDMABUFImageMESA(qemu_egl_display, image, &fd, stride, NULL);
143     eglDestroyImageKHR(qemu_egl_display, image);
144 
145     return fd;
146 }
147 
148 #endif /* CONFIG_OPENGL_DMABUF */
149 
150 /* ---------------------------------------------------------------------- */
151 
152 EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, Window win)
153 {
154     EGLSurface esurface;
155     EGLBoolean b;
156 
157     egl_dbg("eglCreateWindowSurface (x11 win id 0x%lx) ...\n",
158             (unsigned long) win);
159     esurface = eglCreateWindowSurface(qemu_egl_display,
160                                       qemu_egl_config,
161                                       (EGLNativeWindowType)win, NULL);
162     if (esurface == EGL_NO_SURFACE) {
163         fprintf(stderr, "egl: eglCreateWindowSurface failed\n");
164         return NULL;
165     }
166 
167     b = eglMakeCurrent(qemu_egl_display, esurface, esurface, ectx);
168     if (b == EGL_FALSE) {
169         fprintf(stderr, "egl: eglMakeCurrent failed\n");
170         return NULL;
171     }
172 
173     return esurface;
174 }
175 
176 /* ---------------------------------------------------------------------- */
177 
178 int qemu_egl_init_dpy(EGLNativeDisplayType dpy, bool gles, bool debug)
179 {
180     static const EGLint conf_att_gl[] = {
181         EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
182         EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
183         EGL_RED_SIZE,   5,
184         EGL_GREEN_SIZE, 5,
185         EGL_BLUE_SIZE,  5,
186         EGL_ALPHA_SIZE, 0,
187         EGL_NONE,
188     };
189     static const EGLint conf_att_gles[] = {
190         EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
191         EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
192         EGL_RED_SIZE,   5,
193         EGL_GREEN_SIZE, 5,
194         EGL_BLUE_SIZE,  5,
195         EGL_ALPHA_SIZE, 0,
196         EGL_NONE,
197     };
198     EGLint major, minor;
199     EGLBoolean b;
200     EGLint n;
201 
202     if (debug) {
203         egl_debug = 1;
204         setenv("EGL_LOG_LEVEL", "debug", true);
205         setenv("LIBGL_DEBUG", "verbose", true);
206     }
207 
208     egl_dbg("eglGetDisplay (dpy %p) ...\n", dpy);
209     qemu_egl_display = eglGetDisplay(dpy);
210     if (qemu_egl_display == EGL_NO_DISPLAY) {
211         fprintf(stderr, "egl: eglGetDisplay failed\n");
212         return -1;
213     }
214 
215     egl_dbg("eglInitialize ...\n");
216     b = eglInitialize(qemu_egl_display, &major, &minor);
217     if (b == EGL_FALSE) {
218         fprintf(stderr, "egl: eglInitialize failed\n");
219         return -1;
220     }
221 
222     egl_dbg("eglBindAPI ...\n");
223     b = eglBindAPI(gles ? EGL_OPENGL_ES_API : EGL_OPENGL_API);
224     if (b == EGL_FALSE) {
225         fprintf(stderr, "egl: eglBindAPI failed\n");
226         return -1;
227     }
228 
229     egl_dbg("eglChooseConfig ...\n");
230     b = eglChooseConfig(qemu_egl_display,
231                         gles ? conf_att_gles : conf_att_gl,
232                         &qemu_egl_config, 1, &n);
233     if (b == EGL_FALSE || n != 1) {
234         fprintf(stderr, "egl: eglChooseConfig failed\n");
235         return -1;
236     }
237 
238     egl_gles = gles;
239     return 0;
240 }
241 
242 EGLContext qemu_egl_init_ctx(void)
243 {
244     static const EGLint ctx_att_gl[] = {
245         EGL_NONE
246     };
247     static const EGLint ctx_att_gles[] = {
248         EGL_CONTEXT_CLIENT_VERSION, 2,
249         EGL_NONE
250     };
251 
252     EGLContext ectx;
253     EGLBoolean b;
254 
255     egl_dbg("eglCreateContext ...\n");
256     ectx = eglCreateContext(qemu_egl_display, qemu_egl_config, EGL_NO_CONTEXT,
257                             egl_gles ? ctx_att_gles : ctx_att_gl);
258     if (ectx == EGL_NO_CONTEXT) {
259         fprintf(stderr, "egl: eglCreateContext failed\n");
260         return NULL;
261     }
262 
263     b = eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, ectx);
264     if (b == EGL_FALSE) {
265         fprintf(stderr, "egl: eglMakeCurrent failed\n");
266         return NULL;
267     }
268 
269     return ectx;
270 }
271