blob: e5ce77d643b7f3c2a690c86faf165649b0503446 [file] [log] [blame]
Yalong Liu1df84372018-01-24 17:10:12 +08001/*
2 * Copyright 2016 The Chromium OS Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6
7#include "bs_drm.h"
8
9static const char *get_egl_error();
10static const char *get_gl_framebuffer_error();
11
12struct bs_egl {
13 bool setup;
14 EGLDisplay display;
15 EGLContext ctx;
16 bool use_image_flush_external;
17 bool use_dma_buf_import_modifiers;
18
19 // Names are the original gl/egl function names with the prefix chopped off.
20 PFNEGLCREATEIMAGEKHRPROC CreateImageKHR;
21 PFNEGLDESTROYIMAGEKHRPROC DestroyImageKHR;
22 PFNEGLIMAGEFLUSHEXTERNALEXTPROC ImageFlushExternal;
23 PFNGLEGLIMAGETARGETTEXTURE2DOESPROC EGLImageTargetTexture2DOES;
24 PFNEGLCREATESYNCKHRPROC CreateSyncKHR;
25 PFNEGLCLIENTWAITSYNCKHRPROC ClientWaitSyncKHR;
26 PFNEGLDESTROYSYNCKHRPROC DestroySyncKHR;
27};
28
29struct bs_egl_fb {
30 GLuint tex;
31 GLuint fb;
32};
33
34struct bs_egl *bs_egl_new()
35{
36 struct bs_egl *self = calloc(1, sizeof(struct bs_egl));
37 assert(self);
38 self->display = EGL_NO_DISPLAY;
39 self->ctx = EGL_NO_CONTEXT;
40 return self;
41}
42
43void bs_egl_destroy(struct bs_egl **egl)
44{
45 assert(egl);
46 struct bs_egl *self = *egl;
47 assert(self);
48
49 if (self->ctx != EGL_NO_CONTEXT) {
50 assert(self->display != EGL_NO_DISPLAY);
51 eglMakeCurrent(self->display, NULL, NULL, NULL);
52 eglDestroyContext(self->display, self->ctx);
53 }
54
55 if (self->display != EGL_NO_DISPLAY)
56 eglTerminate(self->display);
57
58 free(self);
59 *egl = NULL;
60}
61
62bool bs_egl_setup(struct bs_egl *self)
63{
64 assert(self);
65 assert(!self->setup);
66
67 self->CreateImageKHR = (PFNEGLCREATEIMAGEKHRPROC)eglGetProcAddress("eglCreateImageKHR");
68 self->DestroyImageKHR = (PFNEGLDESTROYIMAGEKHRPROC)eglGetProcAddress("eglDestroyImageKHR");
69 self->ImageFlushExternal =
70 (PFNEGLIMAGEFLUSHEXTERNALEXTPROC)eglGetProcAddress("eglImageFlushExternalEXT");
71 self->EGLImageTargetTexture2DOES =
72 (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)eglGetProcAddress("glEGLImageTargetTexture2DOES");
73 if (!self->CreateImageKHR || !self->DestroyImageKHR || !self->EGLImageTargetTexture2DOES) {
74 bs_debug_error(
75 "eglGetProcAddress returned NULL for a required extension entry point.");
76 return false;
77 }
78
79 self->CreateSyncKHR = (PFNEGLCREATESYNCKHRPROC)eglGetProcAddress("eglCreateSyncKHR");
80 self->ClientWaitSyncKHR =
81 (PFNEGLCLIENTWAITSYNCKHRPROC)eglGetProcAddress("eglClientWaitSyncKHR");
82 self->DestroySyncKHR = (PFNEGLDESTROYSYNCKHRPROC)eglGetProcAddress("eglDestroySyncKHR");
83
84 self->display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
85 if (self->display == EGL_NO_DISPLAY) {
86 bs_debug_error("failed to get egl display");
87 return false;
88 }
89
90 if (!eglInitialize(self->display, NULL /* ignore version */, NULL /* ignore version */)) {
91 bs_debug_error("failed to initialize egl: %s\n", get_egl_error());
92 return false;
93 }
94
95 // Get any EGLConfig. We need one to create a context, but it isn't used to create any
96 // surfaces.
97 const EGLint config_attribs[] = { EGL_SURFACE_TYPE, EGL_DONT_CARE, EGL_RENDERABLE_TYPE,
98 EGL_OPENGL_ES2_BIT, EGL_NONE };
99 EGLConfig egl_config;
100 EGLint num_configs;
101 if (!eglChooseConfig(self->display, config_attribs, &egl_config, 1,
102 &num_configs /* unused but can't be null */)) {
103 bs_debug_error("eglChooseConfig() failed with error: %s", get_egl_error());
104 goto terminate_display;
105 }
106
107 if (!eglBindAPI(EGL_OPENGL_ES_API)) {
108 bs_debug_error("failed to bind OpenGL ES: %s", get_egl_error());
109 goto terminate_display;
110 }
111
112 const EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
113
114 self->ctx = eglCreateContext(self->display, egl_config,
115 EGL_NO_CONTEXT /* no shared context */, context_attribs);
116 if (self->ctx == EGL_NO_CONTEXT) {
117 bs_debug_error("failed to create OpenGL ES Context: %s", get_egl_error());
118 goto terminate_display;
119 }
120
121 if (!eglMakeCurrent(self->display, EGL_NO_SURFACE /* no default draw surface */,
122 EGL_NO_SURFACE /* no default draw read */, self->ctx)) {
123 bs_debug_error("failed to make the OpenGL ES Context current: %s", get_egl_error());
124 goto destroy_context;
125 }
126
127 const char *egl_extensions = eglQueryString(self->display, EGL_EXTENSIONS);
128 if (!bs_egl_has_extension("EGL_KHR_image_base", egl_extensions)) {
129 bs_debug_error("EGL_KHR_image_base extension not supported");
130 goto destroy_context;
131 }
132 if (!bs_egl_has_extension("EGL_EXT_image_dma_buf_import", egl_extensions)) {
133 bs_debug_error("EGL_EXT_image_dma_buf_import extension not supported");
134 goto destroy_context;
135 }
136
137 if (!bs_egl_has_extension("EGL_KHR_fence_sync", egl_extensions) &&
138 !bs_egl_has_extension("EGL_KHR_wait_sync", egl_extensions)) {
139 bs_debug_error("EGL_KHR_fence_sync and EGL_KHR_wait_sync extension not supported");
140 goto destroy_context;
141 }
142
143 if (bs_egl_has_extension("EGL_EXT_image_flush_external", egl_extensions)) {
144 if (!self->ImageFlushExternal) {
145 bs_debug_print("WARNING", __func__, __FILE__, __LINE__,
146 "EGL_EXT_image_flush_external extension is supported, but "
147 "eglGetProcAddress returned NULL.");
148 } else {
149 self->use_image_flush_external = true;
150 }
151 }
152 if (bs_egl_has_extension("EGL_EXT_image_dma_buf_import_modifiers", egl_extensions))
153 self->use_dma_buf_import_modifiers = true;
154
155 const char *gl_extensions = (const char *)glGetString(GL_EXTENSIONS);
156 if (!bs_egl_has_extension("GL_OES_EGL_image", gl_extensions)) {
157 bs_debug_error("GL_OES_EGL_image extension not supported");
158 goto destroy_context;
159 }
160
161 self->setup = true;
162
163 return true;
164
165destroy_context:
166 eglDestroyContext(self->display, self->ctx);
167terminate_display:
168 eglTerminate(self->display);
169 self->display = EGL_NO_DISPLAY;
170 return false;
171}
172
173bool bs_egl_make_current(struct bs_egl *self)
174{
175 assert(self);
176 assert(self->display != EGL_NO_DISPLAY);
177 assert(self->ctx != EGL_NO_CONTEXT);
178 return eglMakeCurrent(self->display, EGL_NO_SURFACE /* No default draw surface */,
179 EGL_NO_SURFACE /* No default draw read */, self->ctx);
180}
181
182EGLImageKHR bs_egl_image_create_gbm(struct bs_egl *self, struct gbm_bo *bo)
183{
184 assert(self);
185 assert(self->CreateImageKHR);
186 assert(self->display != EGL_NO_DISPLAY);
187 assert(bo);
188
189 int fds[GBM_MAX_PLANES];
190 for (size_t plane = 0; plane < gbm_bo_get_num_planes(bo); plane++) {
191 fds[plane] = gbm_bo_get_plane_fd(bo, plane);
192 if (fds[plane] < 0) {
193 bs_debug_error("failed to get fb for bo: %d", fds[plane]);
194 return EGL_NO_IMAGE_KHR;
195 }
196 }
197
198 // When the bo has 3 planes with modifier support, it requires 39 components.
199 EGLint khr_image_attrs[39] = {
200 EGL_WIDTH,
201 gbm_bo_get_width(bo),
202 EGL_HEIGHT,
203 gbm_bo_get_height(bo),
204 EGL_LINUX_DRM_FOURCC_EXT,
205 (int)gbm_bo_get_format(bo),
206 EGL_NONE,
207 };
208
209 size_t attrs_index = 6;
210 for (size_t plane = 0; plane < gbm_bo_get_num_planes(bo); plane++) {
211 khr_image_attrs[attrs_index++] = EGL_DMA_BUF_PLANE0_FD_EXT + plane * 3;
212 khr_image_attrs[attrs_index++] = fds[plane];
213 khr_image_attrs[attrs_index++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT + plane * 3;
214 khr_image_attrs[attrs_index++] = gbm_bo_get_plane_offset(bo, plane);
215 khr_image_attrs[attrs_index++] = EGL_DMA_BUF_PLANE0_PITCH_EXT + plane * 3;
216 khr_image_attrs[attrs_index++] = gbm_bo_get_plane_stride(bo, plane);
217 if (self->use_dma_buf_import_modifiers) {
218 const uint64_t modifier = gbm_bo_get_format_modifier(bo);
219 khr_image_attrs[attrs_index++] =
220 EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT + plane * 2;
221 khr_image_attrs[attrs_index++] = modifier & 0xfffffffful;
222 khr_image_attrs[attrs_index++] =
223 EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT + plane * 2;
224 khr_image_attrs[attrs_index++] = modifier >> 32;
225 }
226 }
227
228 // Instead of disabling the extension completely, allow it to be used
229 // without creating EGLImages with correct attributes on Tegra.
230 // TODO(gsingh): Remove this once Tegra is end-of-life.
231 const char *egl_vendor = eglQueryString(self->display, EGL_VENDOR);
232 if (self->use_image_flush_external && strcmp(egl_vendor, "NVIDIA")) {
233 khr_image_attrs[attrs_index++] = EGL_IMAGE_EXTERNAL_FLUSH_EXT;
234 khr_image_attrs[attrs_index++] = EGL_TRUE;
235 }
236
237 khr_image_attrs[attrs_index++] = EGL_NONE;
238
239 EGLImageKHR image =
240 self->CreateImageKHR(self->display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT,
241 NULL /* no client buffer */, khr_image_attrs);
242
243 if (image == EGL_NO_IMAGE_KHR) {
244 bs_debug_error("failed to make image from target buffer: %s", get_egl_error());
245 return EGL_NO_IMAGE_KHR;
246 }
247
248 for (size_t plane = 0; plane < gbm_bo_get_num_planes(bo); plane++) {
249 close(fds[plane]);
250 }
251
252 return image;
253}
254
255void bs_egl_image_destroy(struct bs_egl *self, EGLImageKHR *image)
256{
257 assert(self);
258 assert(image);
259 assert(*image != EGL_NO_IMAGE_KHR);
260 assert(self->DestroyImageKHR);
261 self->DestroyImageKHR(self->display, *image);
262 *image = EGL_NO_IMAGE_KHR;
263}
264
265bool bs_egl_image_flush_external(struct bs_egl *self, EGLImageKHR image)
266{
267 assert(self);
268 assert(image != EGL_NO_IMAGE_KHR);
269 if (!self->use_image_flush_external)
270 return true;
271 const EGLAttrib attrs[] = { EGL_NONE };
272 return self->ImageFlushExternal(self->display, image, attrs);
273}
274
275struct bs_egl_fb *bs_egl_fb_new(struct bs_egl *self, EGLImageKHR image)
276{
277 assert(self);
278 assert(self->EGLImageTargetTexture2DOES);
279
280 struct bs_egl_fb *fb = calloc(1, sizeof(struct bs_egl_fb));
281 assert(fb);
282
283 glGenTextures(1, &fb->tex);
284 glBindTexture(GL_TEXTURE_2D, fb->tex);
285 self->EGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)image);
286 glBindTexture(GL_TEXTURE_2D, 0);
287
288 glGenFramebuffers(1, &fb->fb);
289 glBindFramebuffer(GL_FRAMEBUFFER, fb->fb);
290 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->tex, 0);
291
292 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
293 bs_debug_error("failed framebuffer check for created target buffer: %s",
294 get_gl_framebuffer_error());
295 glDeleteFramebuffers(1, &fb->fb);
296 glDeleteTextures(1, &fb->tex);
297 free(fb);
298 return NULL;
299 }
300
301 return fb;
302}
303
304void bs_egl_fb_destroy(struct bs_egl_fb **fb)
305{
306 assert(fb);
307 struct bs_egl_fb *self = *fb;
308 assert(self);
309
310 glDeleteFramebuffers(1, &self->fb);
311 glDeleteTextures(1, &self->tex);
312
313 free(self);
314 *fb = NULL;
315}
316
317GLuint bs_egl_fb_name(struct bs_egl_fb *self)
318{
319 assert(self);
320 return self->fb;
321}
322
323bool bs_egl_target_texture2D(struct bs_egl *self, EGLImageKHR image)
324{
325 assert(self);
326 assert(self->EGLImageTargetTexture2DOES);
327 self->EGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)image);
328 GLint error = glGetError();
329 return (error == GL_NO_ERROR);
330}
331
332EGLSyncKHR bs_egl_create_sync(struct bs_egl *self, EGLenum type, const EGLint *attrib_list)
333{
334 return self->CreateSyncKHR(self->display, type, attrib_list);
335}
336
337EGLint bs_egl_wait_sync(struct bs_egl *self, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout)
338{
339 return self->ClientWaitSyncKHR(self->display, sync, flags, timeout);
340}
341
342EGLBoolean bs_egl_destroy_sync(struct bs_egl *self, EGLSyncKHR sync)
343{
344 return self->DestroySyncKHR(self->display, sync);
345}
346
347bool bs_egl_has_extension(const char *extension, const char *extensions)
348{
349 const char *start, *where, *terminator;
350 start = extensions;
351 for (;;) {
352 where = (char *)strstr((const char *)start, extension);
353 if (!where)
354 break;
355 terminator = where + strlen(extension);
356 if (where == start || *(where - 1) == ' ')
357 if (*terminator == ' ' || *terminator == '\0')
358 return true;
359 start = terminator;
360 }
361 return false;
362}
363
364static const char *get_egl_error()
365{
366 switch (eglGetError()) {
367 case EGL_SUCCESS:
368 return "EGL_SUCCESS";
369 case EGL_NOT_INITIALIZED:
370 return "EGL_NOT_INITIALIZED";
371 case EGL_BAD_ACCESS:
372 return "EGL_BAD_ACCESS";
373 case EGL_BAD_ALLOC:
374 return "EGL_BAD_ALLOC";
375 case EGL_BAD_ATTRIBUTE:
376 return "EGL_BAD_ATTRIBUTE";
377 case EGL_BAD_CONTEXT:
378 return "EGL_BAD_CONTEXT";
379 case EGL_BAD_CONFIG:
380 return "EGL_BAD_CONFIG";
381 case EGL_BAD_CURRENT_SURFACE:
382 return "EGL_BAD_CURRENT_SURFACE";
383 case EGL_BAD_DISPLAY:
384 return "EGL_BAD_DISPLAY";
385 case EGL_BAD_SURFACE:
386 return "EGL_BAD_SURFACE";
387 case EGL_BAD_MATCH:
388 return "EGL_BAD_MATCH";
389 case EGL_BAD_PARAMETER:
390 return "EGL_BAD_PARAMETER";
391 case EGL_BAD_NATIVE_PIXMAP:
392 return "EGL_BAD_NATIVE_PIXMAP";
393 case EGL_BAD_NATIVE_WINDOW:
394 return "EGL_BAD_NATIVE_WINDOW";
395 case EGL_CONTEXT_LOST:
396 return "EGL_CONTEXT_LOST";
397 default:
398 return "EGL_???";
399 }
400}
401
402static const char *get_gl_framebuffer_error()
403{
404 switch (glCheckFramebufferStatus(GL_FRAMEBUFFER)) {
405 case GL_FRAMEBUFFER_COMPLETE:
406 return "GL_FRAMEBUFFER_COMPLETE";
407 case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
408 return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
409 case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
410 return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
411 case GL_FRAMEBUFFER_UNSUPPORTED:
412 return "GL_FRAMEBUFFER_UNSUPPORTED";
413 case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
414 return "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
415 default:
416 return "GL_FRAMEBUFFER_???";
417 }
418}