| /* | |
| * Copyright (C) 2012 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. | |
| */ | |
| #include <GLES2/gl2.h> | |
| #include <GLES2/gl2ext.h> | |
| #include <ui/DisplayInfo.h> | |
| #include <gui/SurfaceComposerClient.h> | |
| #include "GLHelper.h" | |
| namespace android { | |
| GLHelper::GLHelper() : | |
| mDisplay(EGL_NO_DISPLAY), | |
| mContext(EGL_NO_CONTEXT), | |
| mDummySurface(EGL_NO_SURFACE), | |
| mConfig(0), | |
| mShaderPrograms(NULL), | |
| mDitherTexture(0) { | |
| } | |
| GLHelper::~GLHelper() { | |
| } | |
| bool GLHelper::setUp(const ShaderDesc* shaderDescs, size_t numShaders) { | |
| bool result; | |
| mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); | |
| if (mDisplay == EGL_NO_DISPLAY) { | |
| fprintf(stderr, "eglGetDisplay error: %#x\n", eglGetError()); | |
| return false; | |
| } | |
| EGLint majorVersion; | |
| EGLint minorVersion; | |
| result = eglInitialize(mDisplay, &majorVersion, &minorVersion); | |
| if (result != EGL_TRUE) { | |
| fprintf(stderr, "eglInitialize error: %#x\n", eglGetError()); | |
| return false; | |
| } | |
| EGLint numConfigs = 0; | |
| EGLint configAttribs[] = { | |
| EGL_SURFACE_TYPE, EGL_WINDOW_BIT, | |
| EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, | |
| EGL_RED_SIZE, 8, | |
| EGL_GREEN_SIZE, 8, | |
| EGL_BLUE_SIZE, 8, | |
| EGL_ALPHA_SIZE, 8, | |
| EGL_NONE | |
| }; | |
| result = eglChooseConfig(mDisplay, configAttribs, &mConfig, 1, | |
| &numConfigs); | |
| if (result != EGL_TRUE) { | |
| fprintf(stderr, "eglChooseConfig error: %#x\n", eglGetError()); | |
| return false; | |
| } | |
| EGLint contextAttribs[] = { | |
| EGL_CONTEXT_CLIENT_VERSION, 2, | |
| EGL_NONE | |
| }; | |
| mContext = eglCreateContext(mDisplay, mConfig, EGL_NO_CONTEXT, | |
| contextAttribs); | |
| if (mContext == EGL_NO_CONTEXT) { | |
| fprintf(stderr, "eglCreateContext error: %#x\n", eglGetError()); | |
| return false; | |
| } | |
| bool resultb = createNamedSurfaceTexture(0, 1, 1, &mDummyGLConsumer, | |
| &mDummySurface); | |
| if (!resultb) { | |
| return false; | |
| } | |
| resultb = makeCurrent(mDummySurface); | |
| if (!resultb) { | |
| return false; | |
| } | |
| resultb = setUpShaders(shaderDescs, numShaders); | |
| if (!resultb) { | |
| return false; | |
| } | |
| return true; | |
| } | |
| void GLHelper::tearDown() { | |
| if (mShaderPrograms != NULL) { | |
| delete[] mShaderPrograms; | |
| mShaderPrograms = NULL; | |
| } | |
| if (mSurfaceComposerClient != NULL) { | |
| mSurfaceComposerClient->dispose(); | |
| mSurfaceComposerClient.clear(); | |
| } | |
| if (mDisplay != EGL_NO_DISPLAY) { | |
| eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, | |
| EGL_NO_CONTEXT); | |
| } | |
| if (mContext != EGL_NO_CONTEXT) { | |
| eglDestroyContext(mDisplay, mContext); | |
| } | |
| if (mDummySurface != EGL_NO_SURFACE) { | |
| eglDestroySurface(mDisplay, mDummySurface); | |
| } | |
| mDisplay = EGL_NO_DISPLAY; | |
| mContext = EGL_NO_CONTEXT; | |
| mDummySurface = EGL_NO_SURFACE; | |
| mDummyGLConsumer.clear(); | |
| mConfig = 0; | |
| } | |
| bool GLHelper::makeCurrent(EGLSurface surface) { | |
| EGLint result; | |
| result = eglMakeCurrent(mDisplay, surface, surface, mContext); | |
| if (result != EGL_TRUE) { | |
| fprintf(stderr, "eglMakeCurrent error: %#x\n", eglGetError()); | |
| return false; | |
| } | |
| EGLint w, h; | |
| eglQuerySurface(mDisplay, surface, EGL_WIDTH, &w); | |
| eglQuerySurface(mDisplay, surface, EGL_HEIGHT, &h); | |
| glViewport(0, 0, w, h); | |
| return true; | |
| } | |
| bool GLHelper::createSurfaceTexture(uint32_t w, uint32_t h, | |
| sp<GLConsumer>* glConsumer, EGLSurface* surface, | |
| GLuint* name) { | |
| if (!makeCurrent(mDummySurface)) { | |
| return false; | |
| } | |
| *name = 0; | |
| glGenTextures(1, name); | |
| if (*name == 0) { | |
| fprintf(stderr, "glGenTextures error: %#x\n", glGetError()); | |
| return false; | |
| } | |
| return createNamedSurfaceTexture(*name, w, h, glConsumer, surface); | |
| } | |
| void GLHelper::destroySurface(EGLSurface* surface) { | |
| if (eglGetCurrentSurface(EGL_READ) == *surface || | |
| eglGetCurrentSurface(EGL_DRAW) == *surface) { | |
| eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, | |
| EGL_NO_CONTEXT); | |
| } | |
| eglDestroySurface(mDisplay, *surface); | |
| *surface = EGL_NO_SURFACE; | |
| } | |
| bool GLHelper::swapBuffers(EGLSurface surface) { | |
| EGLint result; | |
| result = eglSwapBuffers(mDisplay, surface); | |
| if (result != EGL_TRUE) { | |
| fprintf(stderr, "eglSwapBuffers error: %#x\n", eglGetError()); | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool GLHelper::getShaderProgram(const char* name, GLuint* outPgm) { | |
| for (size_t i = 0; i < mNumShaders; i++) { | |
| if (strcmp(mShaderDescs[i].name, name) == 0) { | |
| *outPgm = mShaderPrograms[i]; | |
| return true; | |
| } | |
| } | |
| fprintf(stderr, "unknown shader name: \"%s\"\n", name); | |
| return false; | |
| } | |
| bool GLHelper::createNamedSurfaceTexture(GLuint name, uint32_t w, uint32_t h, | |
| sp<GLConsumer>* glConsumer, EGLSurface* surface) { | |
| sp<IGraphicBufferProducer> producer; | |
| sp<IGraphicBufferConsumer> consumer; | |
| BufferQueue::createBufferQueue(&producer, &consumer); | |
| sp<GLConsumer> glc = new GLConsumer(consumer, name, | |
| GL_TEXTURE_EXTERNAL_OES, false, true); | |
| glc->setDefaultBufferSize(w, h); | |
| producer->setMaxDequeuedBufferCount(2); | |
| glc->setConsumerUsageBits(GRALLOC_USAGE_HW_COMPOSER); | |
| sp<ANativeWindow> anw = new Surface(producer); | |
| EGLSurface s = eglCreateWindowSurface(mDisplay, mConfig, anw.get(), NULL); | |
| if (s == EGL_NO_SURFACE) { | |
| fprintf(stderr, "eglCreateWindowSurface error: %#x\n", eglGetError()); | |
| return false; | |
| } | |
| *glConsumer = glc; | |
| *surface = s; | |
| return true; | |
| } | |
| bool GLHelper::computeWindowScale(uint32_t w, uint32_t h, float* scale) { | |
| sp<IBinder> dpy = mSurfaceComposerClient->getBuiltInDisplay(0); | |
| if (dpy == NULL) { | |
| fprintf(stderr, "SurfaceComposer::getBuiltInDisplay failed.\n"); | |
| return false; | |
| } | |
| DisplayInfo info; | |
| status_t err = mSurfaceComposerClient->getDisplayInfo(dpy, &info); | |
| if (err != NO_ERROR) { | |
| fprintf(stderr, "SurfaceComposer::getDisplayInfo failed: %#x\n", err); | |
| return false; | |
| } | |
| float scaleX = float(info.w) / float(w); | |
| float scaleY = float(info.h) / float(h); | |
| *scale = scaleX < scaleY ? scaleX : scaleY; | |
| return true; | |
| } | |
| bool GLHelper::createWindowSurface(uint32_t w, uint32_t h, | |
| sp<SurfaceControl>* surfaceControl, EGLSurface* surface) { | |
| bool result; | |
| status_t err; | |
| if (mSurfaceComposerClient == NULL) { | |
| mSurfaceComposerClient = new SurfaceComposerClient; | |
| } | |
| err = mSurfaceComposerClient->initCheck(); | |
| if (err != NO_ERROR) { | |
| fprintf(stderr, "SurfaceComposerClient::initCheck error: %#x\n", err); | |
| return false; | |
| } | |
| sp<SurfaceControl> sc = mSurfaceComposerClient->createSurface( | |
| String8("Benchmark"), w, h, PIXEL_FORMAT_RGBA_8888, 0); | |
| if (sc == NULL || !sc->isValid()) { | |
| fprintf(stderr, "Failed to create SurfaceControl.\n"); | |
| return false; | |
| } | |
| float scale; | |
| result = computeWindowScale(w, h, &scale); | |
| if (!result) { | |
| return false; | |
| } | |
| SurfaceComposerClient::openGlobalTransaction(); | |
| err = sc->setLayer(0x7FFFFFFF); | |
| if (err != NO_ERROR) { | |
| fprintf(stderr, "SurfaceComposer::setLayer error: %#x\n", err); | |
| return false; | |
| } | |
| err = sc->setMatrix(scale, 0.0f, 0.0f, scale); | |
| if (err != NO_ERROR) { | |
| fprintf(stderr, "SurfaceComposer::setMatrix error: %#x\n", err); | |
| return false; | |
| } | |
| err = sc->show(); | |
| if (err != NO_ERROR) { | |
| fprintf(stderr, "SurfaceComposer::show error: %#x\n", err); | |
| return false; | |
| } | |
| SurfaceComposerClient::closeGlobalTransaction(); | |
| sp<ANativeWindow> anw = sc->getSurface(); | |
| EGLSurface s = eglCreateWindowSurface(mDisplay, mConfig, anw.get(), NULL); | |
| if (s == EGL_NO_SURFACE) { | |
| fprintf(stderr, "eglCreateWindowSurface error: %#x\n", eglGetError()); | |
| return false; | |
| } | |
| *surfaceControl = sc; | |
| *surface = s; | |
| return true; | |
| } | |
| static bool compileShader(GLenum shaderType, const char* src, | |
| GLuint* outShader) { | |
| GLuint shader = glCreateShader(shaderType); | |
| if (shader == 0) { | |
| fprintf(stderr, "glCreateShader error: %#x\n", glGetError()); | |
| return false; | |
| } | |
| glShaderSource(shader, 1, &src, NULL); | |
| glCompileShader(shader); | |
| GLint compiled = 0; | |
| glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); | |
| if (!compiled) { | |
| GLint infoLen = 0; | |
| glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); | |
| if (infoLen) { | |
| char* buf = new char[infoLen]; | |
| if (buf) { | |
| glGetShaderInfoLog(shader, infoLen, NULL, buf); | |
| fprintf(stderr, "Shader compile log:\n%s\n", buf); | |
| delete[] buf; | |
| } | |
| } | |
| glDeleteShader(shader); | |
| return false; | |
| } | |
| *outShader = shader; | |
| return true; | |
| } | |
| static void printShaderSource(const char* const* src) { | |
| for (size_t i = 0; i < MAX_SHADER_LINES && src[i] != NULL; i++) { | |
| fprintf(stderr, "%3zu: %s\n", i+1, src[i]); | |
| } | |
| } | |
| static const char* makeShaderString(const char* const* src) { | |
| size_t len = 0; | |
| for (size_t i = 0; i < MAX_SHADER_LINES && src[i] != NULL; i++) { | |
| // The +1 is for the '\n' that will be added. | |
| len += strlen(src[i]) + 1; | |
| } | |
| char* result = new char[len+1]; | |
| char* end = result; | |
| for (size_t i = 0; i < MAX_SHADER_LINES && src[i] != NULL; i++) { | |
| strcpy(end, src[i]); | |
| end += strlen(src[i]); | |
| *end = '\n'; | |
| end++; | |
| } | |
| *end = '\0'; | |
| return result; | |
| } | |
| static bool compileShaderLines(GLenum shaderType, const char* const* lines, | |
| GLuint* outShader) { | |
| const char* src = makeShaderString(lines); | |
| bool result = compileShader(shaderType, src, outShader); | |
| if (!result) { | |
| fprintf(stderr, "Shader source:\n"); | |
| printShaderSource(lines); | |
| delete[] src; | |
| return false; | |
| } | |
| delete[] src; | |
| return true; | |
| } | |
| static bool linkShaderProgram(GLuint vs, GLuint fs, GLuint* outPgm) { | |
| GLuint program = glCreateProgram(); | |
| if (program == 0) { | |
| fprintf(stderr, "glCreateProgram error: %#x\n", glGetError()); | |
| return false; | |
| } | |
| glAttachShader(program, vs); | |
| glAttachShader(program, fs); | |
| glLinkProgram(program); | |
| GLint linkStatus = GL_FALSE; | |
| glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); | |
| if (linkStatus != GL_TRUE) { | |
| GLint bufLength = 0; | |
| glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength); | |
| if (bufLength) { | |
| char* buf = new char[bufLength]; | |
| if (buf) { | |
| glGetProgramInfoLog(program, bufLength, NULL, buf); | |
| fprintf(stderr, "Program link log:\n%s\n", buf); | |
| delete[] buf; | |
| } | |
| } | |
| glDeleteProgram(program); | |
| program = 0; | |
| } | |
| *outPgm = program; | |
| return program != 0; | |
| } | |
| bool GLHelper::setUpShaders(const ShaderDesc* shaderDescs, size_t numShaders) { | |
| mShaderPrograms = new GLuint[numShaders]; | |
| bool result = true; | |
| for (size_t i = 0; i < numShaders && result; i++) { | |
| GLuint vs, fs; | |
| result = compileShaderLines(GL_VERTEX_SHADER, | |
| shaderDescs[i].vertexShader, &vs); | |
| if (!result) { | |
| return false; | |
| } | |
| result = compileShaderLines(GL_FRAGMENT_SHADER, | |
| shaderDescs[i].fragmentShader, &fs); | |
| if (!result) { | |
| glDeleteShader(vs); | |
| return false; | |
| } | |
| result = linkShaderProgram(vs, fs, &mShaderPrograms[i]); | |
| glDeleteShader(vs); | |
| glDeleteShader(fs); | |
| } | |
| mNumShaders = numShaders; | |
| mShaderDescs = shaderDescs; | |
| return result; | |
| } | |
| bool GLHelper::getDitherTexture(GLuint* outTexName) { | |
| if (mDitherTexture == 0) { | |
| const uint8_t pattern[] = { | |
| 0, 8, 2, 10, | |
| 12, 4, 14, 6, | |
| 3, 11, 1, 9, | |
| 15, 7, 13, 5 | |
| }; | |
| glGenTextures(1, &mDitherTexture); | |
| glBindTexture(GL_TEXTURE_2D, mDitherTexture); | |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); | |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); | |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, DITHER_KERNEL_SIZE, | |
| DITHER_KERNEL_SIZE, 0, GL_ALPHA, GL_UNSIGNED_BYTE, &pattern); | |
| } | |
| *outTexName = mDitherTexture; | |
| return true; | |
| } | |
| } |