| /* |
| * 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 ATRACE_TAG ATRACE_TAG_GRAPHICS |
| #define LOG_TAG "GLCompositor" |
| |
| #include <sstream> |
| #include <string> |
| #include <vector> |
| |
| #include <cutils/log.h> |
| |
| #define EGL_EGLEXT_PROTOTYPES |
| #define GL_GLEXT_PROTOTYPES |
| |
| #include <EGL/egl.h> |
| #include <EGL/eglext.h> |
| #include <GLES2/gl2.h> |
| #include <GLES2/gl2ext.h> |
| |
| #include <hardware/hardware.h> |
| #include <hardware/hwcomposer.h> |
| |
| #include <ui/GraphicBuffer.h> |
| #include <ui/PixelFormat.h> |
| #include <utils/Trace.h> |
| |
| #include <cutils/properties.h> |
| #include <sync/sync.h> |
| #include <sw_sync.h> |
| |
| #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 <typename T> |
| int AllocResource(std::vector<T> &array) { |
| for (typename std::vector<T>::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 <typename T> |
| void FreeResource(std::vector<T> &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<GraphicBuffer> 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<GLTarget> targets; |
| std::vector<GLComposition *> compositions; |
| |
| std::vector<GLint> 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, Importer *imp) |
| : compositor(owner), importer(imp), target_handle(-1) { |
| } |
| |
| virtual ~GLComposition() { |
| if (compositor == NULL) { |
| return; |
| } |
| |
| // Removes this composition from the owning compositor automatically. |
| std::vector<GLComposition *> &compositions = |
| compositor->priv_->compositions; |
| std::vector<GLComposition *>::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 importer->ReleaseBuffer(bo); |
| } |
| |
| virtual unsigned GetRemainingLayers(int display, unsigned num_needed) const { |
| (void)display; |
| return num_needed; |
| } |
| |
| GLCompositor *compositor; |
| Importer *importer; |
| int target_handle; |
| std::vector<LayerData> 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<RenderingCommand> *commands) { |
| std::vector<seperate_rects::Rect<float> > in_rects; |
| std::vector<seperate_rects::RectSet<uint64_t, float> > 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<float> rect; |
| in_rects.push_back(seperate_rects::Rect<float>( |
| 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<uint64_t, float> &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<float> 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<float> 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<GLTarget>::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<GLComposition *>::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<GraphicBuffer> &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(Importer *importer) { |
| 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, importer); |
| 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 old 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 - alphaCover); \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) { |
| ATRACE_CALL(); |
| int ret = 0; |
| size_t i; |
| std::vector<struct texture_from_handle> layer_textures; |
| std::vector<RenderingCommand> 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(0.0f, 0.0f, 0.0f, 0.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<RenderingCommand>::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 |