/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "bs_drm.h" const char *get_gl_error() { switch (glGetError()) { case GL_NO_ERROR: return "No error has been recorded."; case GL_INVALID_ENUM: return "An unacceptable value is specified for an enumerated argument. The " "offending command is ignored and has no other side effect than to " "set the error flag."; case GL_INVALID_VALUE: return "A numeric argument is out of range. The offending command is " "ignored and has no other side effect than to set the error flag."; case GL_INVALID_OPERATION: return "The specified operation is not allowed in the current state. The " "offending command is ignored and has no other side effect than to " "set the error flag."; case GL_INVALID_FRAMEBUFFER_OPERATION: return "The command is trying to render to or read from the framebuffer " "while the currently bound framebuffer is not framebuffer complete " "(i.e. the return value from glCheckFramebufferStatus is not " "GL_FRAMEBUFFER_COMPLETE). The offending command is ignored and has " "no other side effect than to set the error flag."; case GL_OUT_OF_MEMORY: return "There is not enough memory left to execute the command. The state " "of the GL is undefined, except for the state of the error flags, " "after this error is recorded."; default: return "Unknown error"; } } const char *get_egl_error() { switch (eglGetError()) { case EGL_SUCCESS: return "The last function succeeded without error."; case EGL_NOT_INITIALIZED: return "EGL is not initialized, or could not be initialized, for the " "specified EGL display connection."; case EGL_BAD_ACCESS: return "EGL cannot access a requested resource (for example a context is " "bound in another thread)."; case EGL_BAD_ALLOC: return "EGL failed to allocate resources for the requested operation."; case EGL_BAD_ATTRIBUTE: return "An unrecognized attribute or attribute value was passed in the " "attribute list."; case EGL_BAD_CONTEXT: return "An EGLContext argument does not name a valid EGL rendering " "context."; case EGL_BAD_CONFIG: return "An EGLConfig argument does not name a valid EGL frame buffer " "configuration."; case EGL_BAD_CURRENT_SURFACE: return "The current surface of the calling thread is a window, pixel " "buffer or pixmap that is no longer valid."; case EGL_BAD_DISPLAY: return "An EGLDisplay argument does not name a valid EGL display " "connection."; case EGL_BAD_SURFACE: return "An EGLSurface argument does not name a valid surface (window, " "pixel buffer or pixmap) configured for GL rendering."; case EGL_BAD_MATCH: return "Arguments are inconsistent (for example, a valid context requires " "buffers not supplied by a valid surface)."; case EGL_BAD_PARAMETER: return "One or more argument values are invalid."; case EGL_BAD_NATIVE_PIXMAP: return "A NativePixmapType argument does not refer to a valid native " "pixmap."; case EGL_BAD_NATIVE_WINDOW: return "A NativeWindowType argument does not refer to a valid native " "window."; case EGL_CONTEXT_LOST: return "A power management event has occurred. The application must " "destroy all contexts and reinitialise OpenGL ES state and objects " "to continue rendering."; default: return "Unknown error"; } } struct context { unsigned width; unsigned height; EGLDisplay egl_display; EGLContext egl_ctx; unsigned gl_fb; unsigned gl_rb; }; float f(int i) { int a = i % 40; int b = (i / 40) % 6; switch (b) { case 0: case 1: return 0.0f; case 3: case 4: return 1.0f; case 2: return (a / 40.0f); case 5: return 1.0f - (a / 40.0f); default: return 0.0f; } } void draw(struct context *ctx) { int i; const GLchar *vertexShaderStr = "attribute vec4 vPosition;\n" "attribute vec4 vColor;\n" "varying vec4 vFillColor;\n" "void main() {\n" " gl_Position = vPosition;\n" " vFillColor = vColor;\n" "}\n"; const GLchar *fragmentShaderStr = "precision mediump float;\n" "varying vec4 vFillColor;\n" "void main() {\n" " gl_FragColor = vFillColor;\n" "}\n"; GLint vertexShader, fragmentShader, program, status; vertexShader = glCreateShader(GL_VERTEX_SHADER); if (!vertexShader) { fprintf(stderr, "Failed to create vertex shader. Error=0x%x\n", glGetError()); return; } glShaderSource(vertexShader, 1, &vertexShaderStr, NULL); glCompileShader(vertexShader); glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &status); if (!status) { fprintf(stderr, "Failed to compile vertex shader. Error=0x%x\n", glGetError()); return; } fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); if (!fragmentShader) { fprintf(stderr, "Failed to create fragment shader. Error=0x%x\n", glGetError()); return; } glShaderSource(fragmentShader, 1, &fragmentShaderStr, NULL); glCompileShader(fragmentShader); glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &status); if (!status) { fprintf(stderr, "Failed to compile fragment shader. Error=0x%x\n", glGetError()); return; } program = glCreateProgram(); if (!program) { fprintf(stderr, "Failed to create program.\n"); return; } glAttachShader(program, vertexShader); glAttachShader(program, fragmentShader); glBindAttribLocation(program, 0, "vPosition"); glBindAttribLocation(program, 1, "vColor"); glLinkProgram(program); glGetShaderiv(program, GL_LINK_STATUS, &status); if (!status) { fprintf(stderr, "Failed to link program.\n"); return; } glViewport(0, 0, (GLint)ctx->width, (GLint)ctx->height); for (i = 0; i <= 500; i++) { GLfloat verts[] = { 0.0f, -0.5f, 0.0f, -0.5f, 0.5f, 0.0f, 0.5f, 0.5f, 0.0f }; GLfloat colors[] = { 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f }; glClearColor(f(i), f(i + 80), f(i + 160), 0.0f); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(program); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, verts); glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, colors); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glDrawArrays(GL_TRIANGLES, 0, 3); usleep(1e6 / 120); /* 120 Hz */ glFinish(); unsigned char pixels[4]; glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels); printf("color = %hhu %hhu %hhu %hhu\n", pixels[0], pixels[1], pixels[2], pixels[3]); } glDeleteProgram(program); } int main(int argc, char **argv) { int ret = 0; struct context ctx; EGLint egl_major, egl_minor; const char *extensions; EGLint num_configs; EGLConfig egl_config; ctx.width = 800; ctx.height = 600; const EGLint config_attribs[] = { EGL_RED_SIZE, 1, EGL_GREEN_SIZE, 1, EGL_BLUE_SIZE, 1, EGL_DEPTH_SIZE, 1, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_SURFACE_TYPE, EGL_DONT_CARE, EGL_NONE }; const EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; ctx.egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); if (ctx.egl_display == EGL_NO_DISPLAY) { fprintf(stderr, "failed to get egl display\n"); ret = 1; goto fail; } if (!eglInitialize(ctx.egl_display, &egl_major, &egl_minor)) { fprintf(stderr, "failed to initialize egl: %s\n", get_egl_error()); ret = 1; goto terminate_display; } printf("EGL %d.%d\n", egl_major, egl_minor); printf("EGL %s\n", eglQueryString(ctx.egl_display, EGL_VERSION)); extensions = eglQueryString(ctx.egl_display, EGL_EXTENSIONS); printf("EGL Extensions: %s\n", extensions); if (!eglChooseConfig(ctx.egl_display, config_attribs, NULL, 0, &num_configs)) { fprintf(stderr, "eglChooseConfig() failed with error: %x\n", eglGetError()); goto terminate_display; } if (!eglChooseConfig(ctx.egl_display, config_attribs, &egl_config, 1, &num_configs)) { fprintf(stderr, "eglChooseConfig() failed with error: %x\n", eglGetError()); goto terminate_display; } if (!eglBindAPI(EGL_OPENGL_ES_API)) { fprintf(stderr, "failed to bind OpenGL ES: %s\n", get_egl_error()); ret = 1; goto terminate_display; } if (bs_egl_has_extension("EGL_KHR_no_config_context", extensions)) { ctx.egl_ctx = eglCreateContext(ctx.egl_display, NULL /* No Config */, EGL_NO_CONTEXT /* No shared context */, context_attribs); } else { ctx.egl_ctx = eglCreateContext(ctx.egl_display, egl_config, EGL_NO_CONTEXT /* No shared context */, context_attribs); } if (ctx.egl_ctx == EGL_NO_CONTEXT) { fprintf(stderr, "failed to create OpenGL ES Context: %s\n", get_egl_error()); ret = 1; goto terminate_display; } if (!eglMakeCurrent(ctx.egl_display, EGL_NO_SURFACE /* No default draw surface */, EGL_NO_SURFACE /* No default draw read */, ctx.egl_ctx)) { fprintf(stderr, "failed to make the OpenGL ES Context current: %s\n", get_egl_error()); ret = 1; goto destroy_context; } printf("GL extensions: %s\n", glGetString(GL_EXTENSIONS)); glGenFramebuffers(1, &ctx.gl_fb); glBindFramebuffer(GL_FRAMEBUFFER, ctx.gl_fb); glGenRenderbuffers(1, &ctx.gl_rb); glBindRenderbuffer(GL_RENDERBUFFER, ctx.gl_rb); glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8_OES, ctx.width, ctx.height); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, ctx.gl_rb); if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { fprintf(stderr, "failed to create framebuffer: %s\n", get_gl_error()); ret = 1; goto delete_gl_buffers; } draw(&ctx); delete_gl_buffers: glBindRenderbuffer(GL_RENDERBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0); glDeleteFramebuffers(1, &ctx.gl_fb); glDeleteRenderbuffers(1, &ctx.gl_rb); destroy_context: eglMakeCurrent(ctx.egl_display, NULL, NULL, NULL); eglDestroyContext(ctx.egl_display, ctx.egl_ctx); terminate_display: eglTerminate(ctx.egl_display); fail: return ret; }