/* * 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; } }