| /* |
| * Copyright 2013 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 "GLTest.h" |
| |
| #include <gui/Surface.h> |
| |
| #include <GLES2/gl2.h> |
| |
| namespace android { |
| |
| using Transaction = SurfaceComposerClient::Transaction; |
| |
| static int abs(int value) { |
| return value > 0 ? value : -value; |
| } |
| |
| void GLTest::SetUp() { |
| mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); |
| ASSERT_EQ(EGL_SUCCESS, eglGetError()); |
| ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay); |
| |
| EGLint majorVersion; |
| EGLint minorVersion; |
| EXPECT_TRUE(eglInitialize(mEglDisplay, &majorVersion, &minorVersion)); |
| ASSERT_EQ(EGL_SUCCESS, eglGetError()); |
| RecordProperty("EglVersionMajor", majorVersion); |
| RecordProperty("EglVersionMinor", minorVersion); |
| |
| EGLint numConfigs = 0; |
| EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &mGlConfig, 1, |
| &numConfigs)); |
| ASSERT_EQ(EGL_SUCCESS, eglGetError()); |
| |
| char* displaySecsEnv = getenv("GLTEST_DISPLAY_SECS"); |
| if (displaySecsEnv != nullptr) { |
| mDisplaySecs = atoi(displaySecsEnv); |
| if (mDisplaySecs < 0) { |
| mDisplaySecs = 0; |
| } |
| } else { |
| mDisplaySecs = 0; |
| } |
| |
| if (mDisplaySecs > 0) { |
| mComposerClient = new SurfaceComposerClient; |
| ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); |
| |
| mSurfaceControl = mComposerClient->createSurface( |
| String8("Test Surface"), getSurfaceWidth(), getSurfaceHeight(), |
| PIXEL_FORMAT_RGB_888, 0); |
| |
| ASSERT_TRUE(mSurfaceControl != nullptr); |
| ASSERT_TRUE(mSurfaceControl->isValid()); |
| |
| Transaction t; |
| ASSERT_EQ(NO_ERROR, t.setLayer(mSurfaceControl, 0x7FFFFFFF) |
| .show(mSurfaceControl) |
| .apply()); |
| |
| sp<ANativeWindow> window = mSurfaceControl->getSurface(); |
| mEglSurface = createWindowSurface(mEglDisplay, mGlConfig, window); |
| } else { |
| EGLint pbufferAttribs[] = { |
| EGL_WIDTH, getSurfaceWidth(), |
| EGL_HEIGHT, getSurfaceHeight(), |
| EGL_NONE }; |
| |
| mEglSurface = eglCreatePbufferSurface(mEglDisplay, mGlConfig, |
| pbufferAttribs); |
| } |
| ASSERT_EQ(EGL_SUCCESS, eglGetError()); |
| ASSERT_NE(EGL_NO_SURFACE, mEglSurface); |
| |
| mEglContext = eglCreateContext(mEglDisplay, mGlConfig, EGL_NO_CONTEXT, |
| getContextAttribs()); |
| ASSERT_EQ(EGL_SUCCESS, eglGetError()); |
| ASSERT_NE(EGL_NO_CONTEXT, mEglContext); |
| |
| EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, |
| mEglContext)); |
| ASSERT_EQ(EGL_SUCCESS, eglGetError()); |
| |
| EGLint w, h; |
| EXPECT_TRUE(eglQuerySurface(mEglDisplay, mEglSurface, EGL_WIDTH, &w)); |
| ASSERT_EQ(EGL_SUCCESS, eglGetError()); |
| EXPECT_TRUE(eglQuerySurface(mEglDisplay, mEglSurface, EGL_HEIGHT, &h)); |
| ASSERT_EQ(EGL_SUCCESS, eglGetError()); |
| RecordProperty("EglSurfaceWidth", w); |
| RecordProperty("EglSurfaceHeight", h); |
| |
| glViewport(0, 0, w, h); |
| ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); |
| } |
| |
| void GLTest::TearDown() { |
| // Display the result |
| if (mDisplaySecs > 0 && mEglSurface != EGL_NO_SURFACE) { |
| eglSwapBuffers(mEglDisplay, mEglSurface); |
| sleep(mDisplaySecs); |
| } |
| |
| if (mComposerClient != nullptr) { |
| mComposerClient->dispose(); |
| } |
| if (mEglContext != EGL_NO_CONTEXT) { |
| eglDestroyContext(mEglDisplay, mEglContext); |
| } |
| if (mEglSurface != EGL_NO_SURFACE) { |
| eglDestroySurface(mEglDisplay, mEglSurface); |
| } |
| if (mEglDisplay != EGL_NO_DISPLAY) { |
| eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, |
| EGL_NO_CONTEXT); |
| eglTerminate(mEglDisplay); |
| } |
| ASSERT_EQ(EGL_SUCCESS, eglGetError()); |
| } |
| |
| EGLint const* GLTest::getConfigAttribs() { |
| static const EGLint sDefaultConfigAttribs[] = { |
| EGL_SURFACE_TYPE, EGL_PBUFFER_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_DEPTH_SIZE, 16, |
| EGL_STENCIL_SIZE, 8, |
| EGL_NONE }; |
| |
| return sDefaultConfigAttribs; |
| } |
| |
| EGLint const* GLTest::getContextAttribs() { |
| static const EGLint sDefaultContextAttribs[] = { |
| EGL_CONTEXT_CLIENT_VERSION, 2, |
| EGL_NONE }; |
| |
| return sDefaultContextAttribs; |
| } |
| |
| EGLint GLTest::getSurfaceWidth() { |
| return 512; |
| } |
| |
| EGLint GLTest::getSurfaceHeight() { |
| return 512; |
| } |
| |
| EGLSurface GLTest::createWindowSurface(EGLDisplay display, EGLConfig config, |
| sp<ANativeWindow>& window) const { |
| return eglCreateWindowSurface(display, config, window.get(), nullptr); |
| } |
| |
| ::testing::AssertionResult GLTest::checkPixel(int x, int y, |
| int r, int g, int b, int a, int tolerance) { |
| GLubyte pixel[4]; |
| String8 msg; |
| glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel); |
| GLenum err = glGetError(); |
| if (err != GL_NO_ERROR) { |
| msg += String8::format("error reading pixel: %#x", err); |
| while ((err = glGetError()) != GL_NO_ERROR) { |
| msg += String8::format(", %#x", err); |
| } |
| return ::testing::AssertionFailure(::testing::Message(msg.c_str())); |
| } |
| if (r >= 0 && abs(r - int(pixel[0])) > tolerance) { |
| msg += String8::format("r(%d isn't %d)", pixel[0], r); |
| } |
| if (g >= 0 && abs(g - int(pixel[1])) > tolerance) { |
| if (!msg.empty()) { |
| msg += " "; |
| } |
| msg += String8::format("g(%d isn't %d)", pixel[1], g); |
| } |
| if (b >= 0 && abs(b - int(pixel[2])) > tolerance) { |
| if (!msg.empty()) { |
| msg += " "; |
| } |
| msg += String8::format("b(%d isn't %d)", pixel[2], b); |
| } |
| if (a >= 0 && abs(a - int(pixel[3])) > tolerance) { |
| if (!msg.empty()) { |
| msg += " "; |
| } |
| msg += String8::format("a(%d isn't %d)", pixel[3], a); |
| } |
| if (!msg.empty()) { |
| return ::testing::AssertionFailure(::testing::Message(msg.c_str())); |
| } else { |
| return ::testing::AssertionSuccess(); |
| } |
| } |
| |
| ::testing::AssertionResult GLTest::assertRectEq(const Rect &r1, const Rect &r2, |
| int tolerance) { |
| String8 msg; |
| |
| if (abs(r1.left - r2.left) > tolerance) { |
| msg += String8::format("left(%d isn't %d)", r1.left, r2.left); |
| } |
| if (abs(r1.top - r2.top) > tolerance) { |
| if (!msg.empty()) { |
| msg += " "; |
| } |
| msg += String8::format("top(%d isn't %d)", r1.top, r2.top); |
| } |
| if (abs(r1.right - r2.right) > tolerance) { |
| if (!msg.empty()) { |
| msg += " "; |
| } |
| msg += String8::format("right(%d isn't %d)", r1.right, r2.right); |
| } |
| if (abs(r1.bottom - r2.bottom) > tolerance) { |
| if (!msg.empty()) { |
| msg += " "; |
| } |
| msg += String8::format("bottom(%d isn't %d)", r1.bottom, r2.bottom); |
| } |
| if (!msg.empty()) { |
| msg += String8::format(" R1: [%d %d %d %d] R2: [%d %d %d %d]", |
| r1.left, r1.top, r1.right, r1.bottom, |
| r2.left, r2.top, r2.right, r2.bottom); |
| fprintf(stderr, "assertRectEq: %s\n", msg.c_str()); |
| return ::testing::AssertionFailure(::testing::Message(msg.c_str())); |
| } else { |
| return ::testing::AssertionSuccess(); |
| } |
| } |
| |
| void GLTest::loadShader(GLenum shaderType, const char* pSource, |
| GLuint* outShader) { |
| GLuint shader = glCreateShader(shaderType); |
| ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); |
| if (shader) { |
| glShaderSource(shader, 1, &pSource, nullptr); |
| ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); |
| glCompileShader(shader); |
| ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); |
| GLint compiled = 0; |
| glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); |
| ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); |
| if (!compiled) { |
| GLint infoLen = 0; |
| glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); |
| ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); |
| if (infoLen) { |
| char* buf = (char*) malloc(infoLen); |
| if (buf) { |
| glGetShaderInfoLog(shader, infoLen, nullptr, buf); |
| printf("Shader compile log:\n%s\n", buf); |
| free(buf); |
| FAIL(); |
| } |
| } else { |
| char* buf = (char*) malloc(0x1000); |
| if (buf) { |
| glGetShaderInfoLog(shader, 0x1000, nullptr, buf); |
| printf("Shader compile log:\n%s\n", buf); |
| free(buf); |
| FAIL(); |
| } |
| } |
| glDeleteShader(shader); |
| shader = 0; |
| } |
| } |
| ASSERT_TRUE(shader != 0); |
| *outShader = shader; |
| } |
| |
| void GLTest::createProgram(const char* pVertexSource, |
| const char* pFragmentSource, GLuint* outPgm) { |
| GLuint vertexShader, fragmentShader; |
| { |
| SCOPED_TRACE("compiling vertex shader"); |
| ASSERT_NO_FATAL_FAILURE(loadShader(GL_VERTEX_SHADER, pVertexSource, |
| &vertexShader)); |
| } |
| { |
| SCOPED_TRACE("compiling fragment shader"); |
| ASSERT_NO_FATAL_FAILURE(loadShader(GL_FRAGMENT_SHADER, pFragmentSource, |
| &fragmentShader)); |
| } |
| |
| GLuint program = glCreateProgram(); |
| ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); |
| if (program) { |
| glAttachShader(program, vertexShader); |
| ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); |
| glAttachShader(program, fragmentShader); |
| ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); |
| 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 = (char*) malloc(bufLength); |
| if (buf) { |
| glGetProgramInfoLog(program, bufLength, nullptr, buf); |
| printf("Program link log:\n%s\n", buf); |
| free(buf); |
| FAIL(); |
| } |
| } |
| glDeleteProgram(program); |
| program = 0; |
| } |
| } |
| glDeleteShader(vertexShader); |
| glDeleteShader(fragmentShader); |
| ASSERT_TRUE(program != 0); |
| *outPgm = program; |
| } |
| |
| } // namespace android |