/* * Copyright (c) 2017 Rob Clark * Copyright © 2013 Intel Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sub license, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial portions * of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include "common.h" static struct gbm gbm; WEAK struct gbm_surface * gbm_surface_create_with_modifiers(struct gbm_device *gbm, uint32_t width, uint32_t height, uint32_t format, const uint64_t *modifiers, const unsigned int count); WEAK struct gbm_bo * gbm_bo_create_with_modifiers(struct gbm_device *gbm, uint32_t width, uint32_t height, uint32_t format, const uint64_t *modifiers, const unsigned int count); static struct gbm_bo * init_bo(uint64_t modifier) { struct gbm_bo *bo = NULL; if (gbm_bo_create_with_modifiers) { bo = gbm_bo_create_with_modifiers(gbm.dev, gbm.width, gbm.height, gbm.format, &modifier, 1); } if (!bo) { if (modifier != DRM_FORMAT_MOD_LINEAR) { fprintf(stderr, "Modifiers requested but support isn't available\n"); return NULL; } bo = gbm_bo_create(gbm.dev, gbm.width, gbm.height, gbm.format, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); } if (!bo) { printf("failed to create gbm bo\n"); return NULL; } return bo; } static struct gbm * init_surfaceless(uint64_t modifier) { for (unsigned i = 0; i < ARRAY_SIZE(gbm.bos); i++) { gbm.bos[i] = init_bo(modifier); if (!gbm.bos[i]) return NULL; } return &gbm; } static struct gbm * init_surface(uint64_t modifier) { if (gbm_surface_create_with_modifiers) { gbm.surface = gbm_surface_create_with_modifiers(gbm.dev, gbm.width, gbm.height, gbm.format, &modifier, 1); } if (!gbm.surface) { if (modifier != DRM_FORMAT_MOD_LINEAR) { fprintf(stderr, "Modifiers requested but support isn't available\n"); return NULL; } gbm.surface = gbm_surface_create(gbm.dev, gbm.width, gbm.height, gbm.format, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); } if (!gbm.surface) { printf("failed to create gbm surface\n"); return NULL; } return &gbm; } const struct gbm * init_gbm(int drm_fd, int w, int h, uint32_t format, uint64_t modifier, bool surfaceless) { gbm.dev = gbm_create_device(drm_fd); if (!gbm.dev) return NULL; gbm.format = format; gbm.surface = NULL; gbm.width = w; gbm.height = h; if (surfaceless) return init_surfaceless(modifier); return init_surface(modifier); } static bool has_ext(const char *extension_list, const char *ext) { const char *ptr = extension_list; int len = strlen(ext); if (ptr == NULL || *ptr == '\0') return false; while (true) { ptr = strstr(ptr, ext); if (!ptr) return false; if (ptr[len] == ' ' || ptr[len] == '\0') return true; ptr += len; } } static int match_config_to_visual(EGLDisplay egl_display, EGLint visual_id, EGLConfig *configs, int count) { int i; for (i = 0; i < count; ++i) { EGLint id; if (!eglGetConfigAttrib(egl_display, configs[i], EGL_NATIVE_VISUAL_ID, &id)) continue; if (id == visual_id) return i; } return -1; } static bool egl_choose_config(EGLDisplay egl_display, const EGLint *attribs, EGLint visual_id, EGLConfig *config_out) { EGLint count = 0; EGLint matched = 0; EGLConfig *configs; int config_index = -1; if (!eglGetConfigs(egl_display, NULL, 0, &count) || count < 1) { printf("No EGL configs to choose from.\n"); return false; } configs = malloc(count * sizeof *configs); if (!configs) return false; if (!eglChooseConfig(egl_display, attribs, configs, count, &matched) || !matched) { printf("No EGL configs with appropriate attributes.\n"); goto out; } if (!visual_id) config_index = 0; if (config_index == -1) config_index = match_config_to_visual(egl_display, visual_id, configs, matched); if (config_index != -1) *config_out = configs[config_index]; out: free(configs); if (config_index == -1) return false; return true; } static bool create_framebuffer(const struct egl *egl, struct gbm_bo *bo, struct framebuffer *fb) { assert(egl->eglCreateImageKHR); assert(bo); assert(fb); // 1. Create EGLImage. int fd = gbm_bo_get_fd(bo); if (fd < 0) { printf("failed to get fd for bo: %d\n", fd); return false; } EGLint khr_image_attrs[17] = { EGL_WIDTH, gbm_bo_get_width(bo), EGL_HEIGHT, gbm_bo_get_height(bo), EGL_LINUX_DRM_FOURCC_EXT, (int)gbm_bo_get_format(bo), EGL_DMA_BUF_PLANE0_FD_EXT, fd, EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0, EGL_DMA_BUF_PLANE0_PITCH_EXT, gbm_bo_get_stride(bo), EGL_NONE, EGL_NONE, /* modifier lo */ EGL_NONE, EGL_NONE, /* modifier hi */ EGL_NONE, }; if (egl->modifiers_supported) { const uint64_t modifier = gbm_bo_get_modifier(bo); if (modifier != DRM_FORMAT_MOD_LINEAR) { size_t attrs_index = 12; khr_image_attrs[attrs_index++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT; khr_image_attrs[attrs_index++] = modifier & 0xfffffffful; khr_image_attrs[attrs_index++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT; khr_image_attrs[attrs_index++] = modifier >> 32; } } fb->image = egl->eglCreateImageKHR(egl->display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, NULL /* no client buffer */, khr_image_attrs); if (fb->image == EGL_NO_IMAGE_KHR) { printf("failed to make image from buffer object\n"); return false; } // EGLImage takes the fd ownership. close(fd); // 2. Create GL texture and framebuffer. glGenTextures(1, &fb->tex); glBindTexture(GL_TEXTURE_2D, fb->tex); egl->glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, fb->image); glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glBindTexture(GL_TEXTURE_2D, 0); glGenFramebuffers(1, &fb->fb); glBindFramebuffer(GL_FRAMEBUFFER, fb->fb); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->tex, 0); if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { printf("failed framebuffer check for created target buffer\n"); glDeleteFramebuffers(1, &fb->fb); glDeleteTextures(1, &fb->tex); return false; } return true; } int init_egl(struct egl *egl, const struct gbm *gbm, int samples) { EGLint major, minor; static const EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; const EGLint config_attribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 1, EGL_GREEN_SIZE, 1, EGL_BLUE_SIZE, 1, EGL_ALPHA_SIZE, 0, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_SAMPLES, samples, EGL_NONE }; const char *egl_exts_client, *egl_exts_dpy, *gl_exts; #define get_proc_client(ext, name) do { \ if (has_ext(egl_exts_client, #ext)) \ egl->name = (void *)eglGetProcAddress(#name); \ } while (0) #define get_proc_dpy(ext, name) do { \ if (has_ext(egl_exts_dpy, #ext)) \ egl->name = (void *)eglGetProcAddress(#name); \ } while (0) #define get_proc_gl(ext, name) do { \ if (has_ext(gl_exts, #ext)) \ egl->name = (void *)eglGetProcAddress(#name); \ } while (0) egl_exts_client = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); get_proc_client(EGL_EXT_platform_base, eglGetPlatformDisplayEXT); if (egl->eglGetPlatformDisplayEXT) { egl->display = egl->eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, gbm->dev, NULL); } else { egl->display = eglGetDisplay((void *)gbm->dev); } if (!eglInitialize(egl->display, &major, &minor)) { printf("failed to initialize\n"); return -1; } egl_exts_dpy = eglQueryString(egl->display, EGL_EXTENSIONS); get_proc_dpy(EGL_KHR_image_base, eglCreateImageKHR); get_proc_dpy(EGL_KHR_image_base, eglDestroyImageKHR); get_proc_dpy(EGL_KHR_fence_sync, eglCreateSyncKHR); get_proc_dpy(EGL_KHR_fence_sync, eglDestroySyncKHR); get_proc_dpy(EGL_KHR_fence_sync, eglWaitSyncKHR); get_proc_dpy(EGL_KHR_fence_sync, eglClientWaitSyncKHR); get_proc_dpy(EGL_ANDROID_native_fence_sync, eglDupNativeFenceFDANDROID); egl->modifiers_supported = has_ext(egl_exts_dpy, "EGL_EXT_image_dma_buf_import_modifiers"); printf("Using display %p with EGL version %d.%d\n", egl->display, major, minor); printf("===================================\n"); printf("EGL information:\n"); printf(" version: \"%s\"\n", eglQueryString(egl->display, EGL_VERSION)); printf(" vendor: \"%s\"\n", eglQueryString(egl->display, EGL_VENDOR)); printf(" client extensions: \"%s\"\n", egl_exts_client); printf(" display extensions: \"%s\"\n", egl_exts_dpy); printf("===================================\n"); if (!eglBindAPI(EGL_OPENGL_ES_API)) { printf("failed to bind api EGL_OPENGL_ES_API\n"); return -1; } if (!egl_choose_config(egl->display, config_attribs, gbm->format, &egl->config)) { printf("failed to choose config\n"); return -1; } egl->context = eglCreateContext(egl->display, egl->config, EGL_NO_CONTEXT, context_attribs); if (egl->context == EGL_NO_CONTEXT) { printf("failed to create context\n"); return -1; } if (!gbm->surface) { egl->surface = EGL_NO_SURFACE; } else { egl->surface = eglCreateWindowSurface(egl->display, egl->config, (EGLNativeWindowType)gbm->surface, NULL); if (egl->surface == EGL_NO_SURFACE) { printf("failed to create egl surface\n"); return -1; } } /* connect the context to the surface */ eglMakeCurrent(egl->display, egl->surface, egl->surface, egl->context); gl_exts = (char *) glGetString(GL_EXTENSIONS); printf("OpenGL ES 2.x information:\n"); printf(" version: \"%s\"\n", glGetString(GL_VERSION)); printf(" shading language version: \"%s\"\n", glGetString(GL_SHADING_LANGUAGE_VERSION)); printf(" vendor: \"%s\"\n", glGetString(GL_VENDOR)); printf(" renderer: \"%s\"\n", glGetString(GL_RENDERER)); printf(" extensions: \"%s\"\n", gl_exts); printf("===================================\n"); get_proc_gl(GL_OES_EGL_image, glEGLImageTargetTexture2DOES); get_proc_gl(GL_AMD_performance_monitor, glGetPerfMonitorGroupsAMD); get_proc_gl(GL_AMD_performance_monitor, glGetPerfMonitorCountersAMD); get_proc_gl(GL_AMD_performance_monitor, glGetPerfMonitorGroupStringAMD); get_proc_gl(GL_AMD_performance_monitor, glGetPerfMonitorCounterStringAMD); get_proc_gl(GL_AMD_performance_monitor, glGetPerfMonitorCounterInfoAMD); get_proc_gl(GL_AMD_performance_monitor, glGenPerfMonitorsAMD); get_proc_gl(GL_AMD_performance_monitor, glDeletePerfMonitorsAMD); get_proc_gl(GL_AMD_performance_monitor, glSelectPerfMonitorCountersAMD); get_proc_gl(GL_AMD_performance_monitor, glBeginPerfMonitorAMD); get_proc_gl(GL_AMD_performance_monitor, glEndPerfMonitorAMD); get_proc_gl(GL_AMD_performance_monitor, glGetPerfMonitorCounterDataAMD); if (!gbm->surface) { for (unsigned i = 0; i < ARRAY_SIZE(gbm->bos); i++) { if (!create_framebuffer(egl, gbm->bos[i], &egl->fbs[i])) { printf("failed to create framebuffer\n"); return -1; } } } return 0; } int create_program(const char *vs_src, const char *fs_src) { GLuint vertex_shader, fragment_shader, program; GLint ret; vertex_shader = glCreateShader(GL_VERTEX_SHADER); if (vertex_shader == 0) { printf("vertex shader creation failed!:\n"); return -1; } glShaderSource(vertex_shader, 1, &vs_src, NULL); glCompileShader(vertex_shader); glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &ret); if (!ret) { char *log; printf("vertex shader compilation failed!:\n"); glGetShaderiv(vertex_shader, GL_INFO_LOG_LENGTH, &ret); if (ret > 1) { log = malloc(ret); glGetShaderInfoLog(vertex_shader, ret, NULL, log); printf("%s", log); free(log); } return -1; } fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); if (fragment_shader == 0) { printf("fragment shader creation failed!:\n"); return -1; } glShaderSource(fragment_shader, 1, &fs_src, NULL); glCompileShader(fragment_shader); glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &ret); if (!ret) { char *log; printf("fragment shader compilation failed!:\n"); glGetShaderiv(fragment_shader, GL_INFO_LOG_LENGTH, &ret); if (ret > 1) { log = malloc(ret); glGetShaderInfoLog(fragment_shader, ret, NULL, log); printf("%s", log); free(log); } return -1; } program = glCreateProgram(); glAttachShader(program, vertex_shader); glAttachShader(program, fragment_shader); return program; } int link_program(unsigned program) { GLint ret; glLinkProgram(program); glGetProgramiv(program, GL_LINK_STATUS, &ret); if (!ret) { char *log; printf("program linking failed!:\n"); glGetProgramiv(program, GL_INFO_LOG_LENGTH, &ret); if (ret > 1) { log = malloc(ret); glGetProgramInfoLog(program, ret, NULL, log); printf("%s", log); free(log); } return -1; } return 0; } int64_t get_time_ns(void) { struct timespec tv; clock_gettime(CLOCK_MONOTONIC, &tv); return tv.tv_nsec + tv.tv_sec * NSEC_PER_SEC; }