/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "GLCompositor" #include #include #include #include #define EGL_EGLEXT_PROTOTYPES #define GL_GLEXT_PROTOTYPES #include #include #include #include #include #include #include #include #include #include #include #include "drm_hwcomposer.h" #include "gl_compositor.h" #include "seperate_rects.h" // TODO(zachr): use hwc_drm_bo to turn buffer handles into textures #ifndef EGL_NATIVE_HANDLE_ANDROID_NVX #define EGL_NATIVE_HANDLE_ANDROID_NVX 0x322A #endif #define MAX_OVERLAPPING_LAYERS 64 namespace android { struct GLCompositor::texture_from_handle { EGLImageKHR image; GLuint texture; }; static const char *get_gl_error(void); static const char *get_egl_error(void); static bool has_extension(const char *extension, const char *extensions); template int AllocResource(std::vector &array) { for (typename std::vector::iterator it = array.begin(); it != array.end(); ++it) { if (!it->is_some()) { return std::distance(array.begin(), it); } } array.push_back(T()); return array.size() - 1; } template void FreeResource(std::vector &array, int index) { if (index == (int)array.size() - 1) { array.pop_back(); } else if (index >= 0 && (unsigned)index < array.size()) { array[index].Reset(); } } struct GLTarget { sp fb; EGLImageKHR egl_fb_image; GLuint gl_fb; GLuint gl_fb_tex; bool forgotten; unsigned composition_count; GLTarget() : egl_fb_image(EGL_NO_IMAGE_KHR), gl_fb(0), gl_fb_tex(0), forgotten(true), composition_count(0) { } void Reset() { fb.clear(); egl_fb_image = EGL_NO_IMAGE_KHR; gl_fb = 0; gl_fb_tex = 0; forgotten = true; composition_count = 0; } bool is_some() const { return egl_fb_image != EGL_NO_IMAGE_KHR; } }; struct GLCompositor::priv_data { EGLDisplay egl_display; EGLContext egl_ctx; EGLDisplay saved_egl_display; EGLContext saved_egl_ctx; EGLSurface saved_egl_read; EGLSurface saved_egl_draw; int current_target; std::vector targets; std::vector compositions; std::vector blend_programs; GLuint vertex_buffer; priv_data() : egl_display(EGL_NO_DISPLAY), egl_ctx(EGL_NO_CONTEXT), saved_egl_display(EGL_NO_DISPLAY), saved_egl_ctx(EGL_NO_CONTEXT), saved_egl_read(EGL_NO_SURFACE), saved_egl_draw(EGL_NO_SURFACE), current_target(-1) { } }; class GLComposition : public Composition { public: struct LayerData { hwc_layer_1 layer; hwc_drm_bo bo; }; GLComposition(GLCompositor *owner) : compositor(owner), target_handle(-1) { } virtual ~GLComposition() { if (compositor == NULL) { return; } // Removes this composition from the owning compositor automatically. std::vector &compositions = compositor->priv_->compositions; std::vector::iterator it = std::find(compositions.begin(), compositions.end(), this); if (it != compositions.end()) { compositions.erase(it); } GLTarget *target = &compositor->priv_->targets[target_handle]; target->composition_count--; compositor->CheckAndDestroyTarget(target_handle); } virtual int AddLayer(int display, hwc_layer_1 *layer, hwc_drm_bo *bo) { (void)display; if (layer->compositionType != HWC_OVERLAY) { ALOGE("Must add layers with compositionType == HWC_OVERLAY"); return 1; } if (layer->handle == 0) { ALOGE("Must add layers with valid buffer handle"); return 1; } layer_data.push_back(LayerData()); LayerData &layer_datum = layer_data.back(); layer_datum.layer = *layer; layer_datum.bo = *bo; return 0; } virtual unsigned GetRemainingLayers(int display, unsigned num_needed) const { (void)display; return num_needed; } GLCompositor *compositor; int target_handle; std::vector layer_data; }; struct RenderingCommand { struct TextureSource { unsigned texture_index; float crop_bounds[4]; float alpha; }; float bounds[4]; unsigned texture_count; TextureSource textures[MAX_OVERLAPPING_LAYERS]; RenderingCommand() : texture_count(0) { } }; static void ConstructCommands(const GLComposition &composition, std::vector *commands) { std::vector > in_rects; std::vector > out_rects; int i; for (unsigned rect_index = 0; rect_index < composition.layer_data.size(); rect_index++) { const struct hwc_layer_1 &layer = composition.layer_data[rect_index].layer; seperate_rects::Rect rect; in_rects.push_back(seperate_rects::Rect( layer.displayFrame.left, layer.displayFrame.top, layer.displayFrame.right, layer.displayFrame.bottom)); } seperate_frects_64(in_rects, &out_rects); for (unsigned rect_index = 0; rect_index < out_rects.size(); rect_index++) { const seperate_rects::RectSet &out_rect = out_rects[rect_index]; commands->push_back(RenderingCommand()); RenderingCommand &cmd = commands->back(); memcpy(cmd.bounds, out_rect.rect.bounds, sizeof(cmd.bounds)); uint64_t tex_set = out_rect.id_set.getBits(); for (unsigned i = composition.layer_data.size() - 1; tex_set != 0x0; i--) { if (tex_set & (0x1 << i)) { tex_set &= ~(0x1 << i); const struct hwc_layer_1 &layer = composition.layer_data[i].layer; seperate_rects::Rect display_rect( layer.displayFrame.left, layer.displayFrame.top, layer.displayFrame.right, layer.displayFrame.bottom); float display_size[2] = { display_rect.bounds[2] - display_rect.bounds[0], display_rect.bounds[3] - display_rect.bounds[1]}; seperate_rects::Rect crop_rect( layer.sourceCropf.left, layer.sourceCropf.top, layer.sourceCropf.right, layer.sourceCropf.bottom); float crop_size[2] = {crop_rect.bounds[2] - crop_rect.bounds[0], crop_rect.bounds[3] - crop_rect.bounds[1]}; RenderingCommand::TextureSource &src = cmd.textures[cmd.texture_count]; cmd.texture_count++; src.texture_index = i; for (int b = 0; b < 4; b++) { float bound_percent = (cmd.bounds[b] - display_rect.bounds[b % 2]) / display_size[b % 2]; src.crop_bounds[b] = crop_rect.bounds[b % 2] + bound_percent * crop_size[b % 2]; } if (layer.blending == HWC_BLENDING_NONE) { src.alpha = 1.0f; // This layer is opaque. There is no point in using layers below this // one. break; } src.alpha = layer.planeAlpha / 255.0f; } } } } GLCompositor::GLCompositor() { priv_ = new priv_data; } GLCompositor::~GLCompositor() { if (BeginContext()) { goto destroy_ctx; } glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindTexture(GL_TEXTURE_2D, 0); for (std::vector::iterator it = priv_->targets.end(); it != priv_->targets.begin(); it = priv_->targets.end()) { --it; glDeleteFramebuffers(1, &it->gl_fb); glDeleteTextures(1, &it->gl_fb_tex); eglDestroyImageKHR(priv_->egl_display, it->egl_fb_image); priv_->targets.erase(it); } for (std::vector::iterator it = priv_->compositions.end(); it != priv_->compositions.begin(); it = priv_->compositions.end()) { --it; // Prevents compositor from trying to erase itself (*it)->compositor = NULL; delete *it; priv_->compositions.erase(it); } destroy_ctx: eglMakeCurrent(priv_->egl_display, EGL_NO_SURFACE /* No default draw surface */, EGL_NO_SURFACE /* No default draw read */, EGL_NO_CONTEXT); eglDestroyContext(priv_->egl_display, priv_->egl_ctx); EndContext(); delete priv_; } int GLCompositor::Init() { int ret = 0; const char *egl_extensions; const char *gl_extensions; EGLint num_configs; EGLint attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE, EGL_NONE}; EGLConfig egl_config; // clang-format off const GLfloat verts[] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f }; // clang-format on const EGLint config_attribs[] = {EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_NONE}; const EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE}; priv_->egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); if (priv_->egl_display == EGL_NO_DISPLAY) { ALOGE("Failed to get egl display"); ret = 1; goto out; } if (!eglInitialize(priv_->egl_display, NULL, NULL)) { ALOGE("Failed to initialize egl: %s", get_egl_error()); ret = 1; goto out; } egl_extensions = eglQueryString(priv_->egl_display, EGL_EXTENSIONS); // These extensions are all technically required but not always reported due // to meta EGL filtering them out. if (!has_extension("EGL_KHR_image_base", egl_extensions)) ALOGW("EGL_KHR_image_base extension not supported"); if (!has_extension("EGL_ANDROID_image_native_buffer", egl_extensions)) ALOGW("EGL_ANDROID_image_native_buffer extension not supported"); if (!has_extension("EGL_ANDROID_native_fence_sync", egl_extensions)) ALOGW("EGL_ANDROID_native_fence_sync extension not supported"); if (!eglChooseConfig(priv_->egl_display, config_attribs, &egl_config, 1, &num_configs)) { ALOGE("eglChooseConfig() failed with error: %s", get_egl_error()); goto out; } priv_->egl_ctx = eglCreateContext(priv_->egl_display, egl_config, EGL_NO_CONTEXT /* No shared context */, context_attribs); if (priv_->egl_ctx == EGL_NO_CONTEXT) { ALOGE("Failed to create OpenGL ES Context: %s", get_egl_error()); ret = 1; goto out; } ret = BeginContext(); if (ret) goto out; gl_extensions = (const char *)glGetString(GL_EXTENSIONS); if (!has_extension("GL_OES_EGL_image", gl_extensions)) ALOGW("GL_OES_EGL_image extension not supported"); glGenBuffers(1, &priv_->vertex_buffer); glBindBuffer(GL_ARRAY_BUFFER, priv_->vertex_buffer); glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); if (GenerateShaders()) { ret = 1; goto end_ctx; } end_ctx: EndContext(); out: return ret; } Targeting *GLCompositor::targeting() { return (Targeting *)this; } int GLCompositor::CreateTarget(sp &buffer) { int ret; ret = BeginContext(); if (ret) return -1; int target_handle = AllocResource(priv_->targets); GLTarget *target = &priv_->targets[target_handle]; target->fb = buffer; target->egl_fb_image = eglCreateImageKHR( priv_->egl_display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, (EGLClientBuffer)target->fb->getNativeBuffer(), NULL /* no attribs */); if (target->egl_fb_image == EGL_NO_IMAGE_KHR) { ALOGE("Failed to make image from target buffer: %s", get_egl_error()); ret = -1; goto fail_create_image; } glGenTextures(1, &target->gl_fb_tex); glBindTexture(GL_TEXTURE_2D, target->gl_fb_tex); glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)target->egl_fb_image); glBindTexture(GL_TEXTURE_2D, 0); glGenFramebuffers(1, &target->gl_fb); glBindFramebuffer(GL_FRAMEBUFFER, target->gl_fb); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, target->gl_fb_tex, 0); if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { ALOGE("Failed framebuffer check for created target buffer"); ret = 1; goto fail_framebuffer_status; } target->forgotten = false; ret = target_handle; goto out; fail_framebuffer_status: glBindFramebuffer(GL_FRAMEBUFFER, 0); glDeleteFramebuffers(1, &target->gl_fb); glDeleteTextures(1, &target->gl_fb_tex); eglDestroyImageKHR(priv_->egl_display, target->egl_fb_image); target->gl_fb = 0; target->gl_fb_tex = 0; target->egl_fb_image = EGL_NO_IMAGE_KHR; fail_create_image: target->fb.clear(); FreeResource(priv_->targets, target_handle); out: EndContext(); return ret; } void GLCompositor::SetTarget(int target_handle) { if (target_handle >= 0 && (unsigned)target_handle < priv_->targets.size()) { GLTarget *target = &priv_->targets[target_handle]; if (target->is_some()) { priv_->current_target = target_handle; return; } } priv_->current_target = -1; } void GLCompositor::ForgetTarget(int target_handle) { if (target_handle >= 0 && (unsigned)target_handle < priv_->targets.size()) { if (target_handle == priv_->current_target) { priv_->current_target = -1; } GLTarget *target = &priv_->targets[target_handle]; if (target->is_some()) { target->forgotten = true; CheckAndDestroyTarget(target_handle); return; } } ALOGE("Failed to forget target because of invalid handle"); } void GLCompositor::CheckAndDestroyTarget(int target_handle) { GLTarget *target = &priv_->targets[target_handle]; if (target->composition_count == 0 && target->forgotten) { if (BeginContext() == 0) { glDeleteFramebuffers(1, &target->gl_fb); glDeleteTextures(1, &target->gl_fb_tex); eglDestroyImageKHR(priv_->egl_display, target->egl_fb_image); EndContext(); } FreeResource(priv_->targets, target_handle); } } Composition *GLCompositor::CreateComposition() { if (priv_->current_target >= 0 && (unsigned)priv_->current_target < priv_->targets.size()) { GLTarget *target = &priv_->targets[priv_->current_target]; if (target->is_some()) { GLComposition *composition = new GLComposition(this); composition->target_handle = priv_->current_target; target->composition_count++; priv_->compositions.push_back(composition); return composition; } } ALOGE("Failed to create composition because of invalid target handle %d", priv_->current_target); return NULL; } int GLCompositor::QueueComposition(Composition *composition) { if (composition) { int ret = DoComposition(*(GLComposition *)composition); delete composition; return ret; } ALOGE("Failed to queue composition because of invalid composition handle"); return -EINVAL; } int GLCompositor::Composite() { return 0; } int GLCompositor::BeginContext() { priv_->saved_egl_display = eglGetCurrentDisplay(); priv_->saved_egl_ctx = eglGetCurrentContext(); if (priv_->saved_egl_display != priv_->egl_display || priv_->saved_egl_ctx != priv_->egl_ctx) { priv_->saved_egl_read = eglGetCurrentSurface(EGL_READ); priv_->saved_egl_draw = eglGetCurrentSurface(EGL_DRAW); } else { return 0; } if (!eglMakeCurrent(priv_->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, priv_->egl_ctx)) { ALOGE("Failed to make the OpenGL ES Context current: %s", get_egl_error()); return 1; } return 0; } int GLCompositor::EndContext() { if (priv_->saved_egl_display != eglGetCurrentDisplay() || priv_->saved_egl_ctx != eglGetCurrentContext()) { if (!eglMakeCurrent(priv_->saved_egl_display, priv_->saved_egl_read, priv_->saved_egl_draw, priv_->saved_egl_ctx)) { ALOGE("Failed to make the OpenGL ES Context current: %s", get_egl_error()); return 1; } } return 0; } GLint CompileAndCheckShader(GLenum type, unsigned source_count, const GLchar **sources, std::string *shader_log) { GLint status; GLint shader = glCreateShader(type); if (!shader) { *shader_log = "glCreateShader failed"; return 0; } glShaderSource(shader, source_count, sources, NULL); glCompileShader(shader); glGetShaderiv(shader, GL_COMPILE_STATUS, &status); if (!status) { if (shader_log) { GLint log_length; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length); shader_log->resize(log_length); glGetShaderInfoLog(shader, log_length, NULL, &(*shader_log)[0]); } glDeleteShader(shader); return 0; } return shader; } int GLCompositor::GenerateShaders() { // Limits: GL_MAX_VARYING_COMPONENTS, GL_MAX_TEXTURE_IMAGE_UNITS, // GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS // clang-format off const GLchar *shader_preamble = "#version 300 es\n#define LAYER_COUNT "; const GLchar *vertex_shader_source = "\n" "precision mediump int; \n" "uniform vec4 uViewport; \n" "uniform sampler2D uLayerTextures[LAYER_COUNT]; \n" "uniform vec4 uLayerCrop[LAYER_COUNT]; \n" "in vec2 vPosition; \n" "in vec2 vTexCoords; \n" "out vec2 fTexCoords[LAYER_COUNT]; \n" "void main() { \n" " for (int i = 0; i < LAYER_COUNT; i++) { \n" " fTexCoords[i] = (uLayerCrop[i].xy + vTexCoords * uLayerCrop[i].zw) / \n" " vec2(textureSize(uLayerTextures[i], 0)); \n" " } \n" " vec2 scaledPosition = uViewport.xy + vPosition * uViewport.zw; \n" " gl_Position = vec4(scaledPosition * vec2(2.0) - vec2(1.0), 0.0, 1.0); \n" "} \n"; const GLchar *fragment_shader_source = "\n" "precision mediump float; \n" "uniform sampler2D uLayerTextures[LAYER_COUNT]; \n" "uniform float uLayerAlpha[LAYER_COUNT]; \n" "in vec2 fTexCoords[LAYER_COUNT]; \n" "out vec4 oFragColor; \n" "void main() { \n" " vec3 color = vec3(0.0, 0.0, 0.0); \n" " float alphaCover = 1.0; \n" " for (int i = 0; i < LAYER_COUNT; i++) { \n" " vec4 texSample = texture(uLayerTextures[i], fTexCoords[i]); \n" " float a = texSample.a * uLayerAlpha[i]; \n" " color += a * alphaCover * texSample.rgb; \n" " alphaCover *= 1.0 - a; \n" " if (alphaCover <= 0.5/255.0) \n" " break; \n" " } \n" " oFragColor = vec4(color, 1.0); \n" "} \n"; // clang-format on int i, ret = 1; GLint max_texture_images, vertex_shader, fragment_shader, program, status; std::string shader_log; glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &max_texture_images); for (i = 1; i <= max_texture_images; i++) { std::ostringstream layer_count_formatter; layer_count_formatter << i; std::string layer_count(layer_count_formatter.str()); const GLchar *shader_sources[3] = {shader_preamble, layer_count.c_str(), NULL}; shader_sources[2] = vertex_shader_source; vertex_shader = CompileAndCheckShader(GL_VERTEX_SHADER, 3, shader_sources, ret ? &shader_log : NULL); if (!vertex_shader) { if (ret) { ALOGE("Failed to make vertex shader:\n%s", shader_log.c_str()); } break; } shader_sources[2] = fragment_shader_source; fragment_shader = CompileAndCheckShader( GL_FRAGMENT_SHADER, 3, shader_sources, ret ? &shader_log : NULL); if (!fragment_shader) { if (ret) { ALOGE("Failed to make fragment shader:\n%s", shader_log.c_str()); } goto delete_vs; } program = glCreateProgram(); if (!program) { if (ret) ALOGE("Failed to create program %s", get_gl_error()); goto delete_fs; } glAttachShader(program, vertex_shader); glAttachShader(program, fragment_shader); glBindAttribLocation(program, 0, "vPosition"); glBindAttribLocation(program, 1, "vTexCoords"); glLinkProgram(program); glDetachShader(program, vertex_shader); glDeleteShader(vertex_shader); glDetachShader(program, fragment_shader); glDeleteShader(fragment_shader); glGetProgramiv(program, GL_LINK_STATUS, &status); if (!status) { if (ret) { GLint log_length; glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length); std::string program_log(log_length, ' '); glGetProgramInfoLog(program, log_length, NULL, &program_log[0]); ALOGE("Failed to link program: \n%s", program_log.c_str()); } glDeleteProgram(program); break; } ret = 0; priv_->blend_programs.push_back(program); continue; delete_fs: glDeleteShader(fragment_shader); delete_vs: glDeleteShader(vertex_shader); if (ret) break; } return ret; } int GLCompositor::DoComposition(const GLComposition &composition) { int ret = 0; size_t i; std::vector layer_textures; std::vector commands; if (composition.layer_data.size() == 0) { return -EALREADY; } if (BeginContext()) { return -EINVAL; } GLTarget *target = &priv_->targets[composition.target_handle]; GLint frame_width = target->fb->getWidth(); GLint frame_height = target->fb->getHeight(); EGLSyncKHR finished_sync; for (i = 0; i < composition.layer_data.size(); i++) { const struct hwc_layer_1 *layer = &composition.layer_data[i].layer; if (ret) { if (layer->acquireFenceFd >= 0) close(layer->acquireFenceFd); continue; } layer_textures.push_back(texture_from_handle()); ret = CreateTextureFromHandle(layer->handle, &layer_textures.back()); if (!ret) { ret = DoFenceWait(layer->acquireFenceFd); } if (ret) { layer_textures.pop_back(); ret = -EINVAL; } } if (ret) { goto destroy_textures; } ConstructCommands(composition, &commands); glBindFramebuffer(GL_FRAMEBUFFER, target->gl_fb); glViewport(0, 0, frame_width, frame_height); glClearColor(1.0f, 1.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); glBindBuffer(GL_ARRAY_BUFFER, priv_->vertex_buffer); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, NULL); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (void *)(sizeof(float) * 2)); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); for (std::vector::iterator it = commands.begin(); it != commands.end(); ++it) { const RenderingCommand &cmd = *it; if (cmd.texture_count <= 0) { continue; } // TODO(zachr): handle the case of too many overlapping textures for one // area by falling back to rendering as many layers as possible using // multiple blending passes. if (cmd.texture_count > priv_->blend_programs.size()) { ALOGE("Too many layers to render in one area"); continue; } GLint program = priv_->blend_programs[cmd.texture_count - 1]; glUseProgram(program); GLint gl_viewport_loc = glGetUniformLocation(program, "uViewport"); GLint gl_tex_loc = glGetUniformLocation(program, "uLayerTextures"); GLint gl_crop_loc = glGetUniformLocation(program, "uLayerCrop"); GLint gl_alpha_loc = glGetUniformLocation(program, "uLayerAlpha"); glUniform4f(gl_viewport_loc, cmd.bounds[0] / (float)frame_width, cmd.bounds[1] / (float)frame_height, (cmd.bounds[2] - cmd.bounds[0]) / (float)frame_width, (cmd.bounds[3] - cmd.bounds[1]) / (float)frame_height); for (unsigned src_index = 0; src_index < cmd.texture_count; src_index++) { const RenderingCommand::TextureSource &src = cmd.textures[src_index]; glUniform1f(gl_alpha_loc + src_index, src.alpha); glUniform4f(gl_crop_loc + src_index, src.crop_bounds[0], src.crop_bounds[1], src.crop_bounds[2] - src.crop_bounds[0], src.crop_bounds[3] - src.crop_bounds[1]); glUniform1i(gl_tex_loc + src_index, src_index); glActiveTexture(GL_TEXTURE0 + src_index); glBindTexture(GL_TEXTURE_2D, layer_textures[src.texture_index].texture); } glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); for (unsigned src_index = 0; src_index < cmd.texture_count; src_index++) { glActiveTexture(GL_TEXTURE0 + src_index); glBindTexture(GL_TEXTURE_2D, 0); } } glActiveTexture(GL_TEXTURE0); glDisableVertexAttribArray(0); glDisableVertexAttribArray(1); glBindBuffer(GL_ARRAY_BUFFER, 0); glUseProgram(0); glBindFramebuffer(GL_FRAMEBUFFER, 0); finished_sync = eglCreateSyncKHR(priv_->egl_display, EGL_SYNC_NATIVE_FENCE_ANDROID, NULL); if (finished_sync != EGL_NO_SYNC_KHR) { glFlush(); // Creates the syncpoint ret = eglDupNativeFenceFDANDROID(priv_->egl_display, finished_sync); eglDestroySyncKHR(priv_->egl_display, finished_sync); if (ret != EGL_NO_NATIVE_FENCE_FD_ANDROID) { goto destroy_textures; } } // Used as a fallback if the native sync fence fails. ret = -EALREADY; glFinish(); destroy_textures: for (i = 0; i < layer_textures.size(); i++) DestroyTextureFromHandle(layer_textures[i]); EndContext(); return ret; } int GLCompositor::DoFenceWait(int acquireFenceFd) { int ret = 0; EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, acquireFenceFd, EGL_NONE}; EGLSyncKHR egl_sync = eglCreateSyncKHR( priv_->egl_display, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); if (egl_sync == EGL_NO_SYNC_KHR) { ALOGE("Failed to make EGLSyncKHR from acquireFenceFd: %s", get_egl_error()); close(acquireFenceFd); return 1; } EGLint success = eglWaitSyncKHR(priv_->egl_display, egl_sync, 0); if (success == EGL_FALSE) { ALOGE("Failed to wait for acquire: %s", get_egl_error()); ret = 1; } eglDestroySyncKHR(priv_->egl_display, egl_sync); return ret; } int GLCompositor::CreateTextureFromHandle(buffer_handle_t handle, struct texture_from_handle *tex) { EGLImageKHR image = eglCreateImageKHR( priv_->egl_display, EGL_NO_CONTEXT, EGL_NATIVE_HANDLE_ANDROID_NVX, (EGLClientBuffer)handle, NULL /* no attribs */); if (image == EGL_NO_IMAGE_KHR) { ALOGE("Failed to make image %s %p", get_egl_error(), handle); return 1; } glGenTextures(1, &tex->texture); glBindTexture(GL_TEXTURE_2D, tex->texture); glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)image); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); tex->image = image; return 0; } void GLCompositor::DestroyTextureFromHandle( const struct texture_from_handle &tex) { glDeleteTextures(1, &tex.texture); eglDestroyImageKHR(priv_->egl_display, tex.image); } static const char *get_gl_error(void) { switch (glGetError()) { case GL_NO_ERROR: return "GL_NO_ERROR"; case GL_INVALID_ENUM: return "GL_INVALID_ENUM"; case GL_INVALID_VALUE: return "GL_INVALID_VALUE"; case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION"; case GL_INVALID_FRAMEBUFFER_OPERATION: return "GL_INVALID_FRAMEBUFFER_OPERATION"; case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY"; default: return "Unknown error"; } } static const char *get_egl_error(void) { switch (eglGetError()) { case EGL_SUCCESS: return "EGL_SUCCESS"; case EGL_NOT_INITIALIZED: return "EGL_NOT_INITIALIZED"; case EGL_BAD_ACCESS: return "EGL_BAD_ACCESS"; case EGL_BAD_ALLOC: return "EGL_BAD_ALLOC"; case EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE"; case EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT"; case EGL_BAD_CONFIG: return "EGL_BAD_CONFIG"; case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE"; case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY"; case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE"; case EGL_BAD_MATCH: return "EGL_BAD_MATCH"; case EGL_BAD_PARAMETER: return "EGL_BAD_PARAMETER"; case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP"; case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW"; case EGL_CONTEXT_LOST: return "EGL_CONTEXT_LOST"; default: return "Unknown error"; } } static bool has_extension(const char *extension, const char *extensions) { const char *start, *where, *terminator; start = extensions; for (;;) { where = (char *)strstr((const char *)start, extension); if (!where) break; terminator = where + strlen(extension); if (where == start || *(where - 1) == ' ') if (*terminator == ' ' || *terminator == '\0') return true; start = terminator; } return false; } } // namespace android