#include "bcm_host.h" #include "GLES/gl.h" #include "EGL/egl.h" #include "EGL/eglext.h" #include "GLES2/gl2.h" #include #include #include #include static uint32_t frame_width = 0; static uint32_t frame_height = 0; #define SHOW_ERROR gles_show_error(); static void SetOrtho(GLfloat m[4][4], GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat near, GLfloat far, GLfloat scale_x, GLfloat scale_y); static const char* vertex_shader = "uniform mat4 u_vp_matrix; \n" "attribute vec4 a_position; \n" "attribute vec2 a_texcoord; \n" "varying mediump vec2 v_texcoord; \n" "void main() \n" "{ \n" " v_texcoord = a_texcoord; \n" " gl_Position = u_vp_matrix * a_position; \n" "} \n"; static const char* fragment_shader = "varying mediump vec2 v_texcoord; \n" "uniform sampler2D u_texture; \n" "void main() \n" "{ \n" " gl_FragColor = texture2D(u_texture, v_texcoord); \n" "} \n"; static const GLfloat vertices[] = { -0.5f, -0.5f, 0.0f, +0.5f, -0.5f, 0.0f, +0.5f, +0.5f, 0.0f, -0.5f, +0.5f, 0.0f, }; #define TEX_WIDTH 1024 #define TEX_HEIGHT 512 static const GLfloat uvs[8]; static const GLushort indices[] = { 0, 1, 2, 0, 2, 3, }; static const int kVertexCount = 4; static const int kIndexCount = 6; void Create_uvs(GLfloat * matrix, GLfloat max_u, GLfloat max_v) { memset(matrix,0,sizeof(GLfloat)*8); matrix[3]=max_v; matrix[4]=max_u; matrix[5]=max_v; matrix[6]=max_u; } void gles_show_error() { GLenum error = GL_NO_ERROR; error = glGetError(); if (GL_NO_ERROR != error) printf("GL Error %x encountered!\n", error); } static GLuint CreateShader(GLenum type, const char *shader_src) { GLuint shader = glCreateShader(type); if(!shader) return 0; // Load and compile the shader source glShaderSource(shader, 1, &shader_src, NULL); glCompileShader(shader); // Check the compile status GLint compiled = 0; glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); if(!compiled) { GLint info_len = 0; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_len); if(info_len > 1) { char* info_log = (char *)malloc(sizeof(char) * info_len); glGetShaderInfoLog(shader, info_len, NULL, info_log); // TODO(dspringer): We could really use a logging API. printf("Error compiling shader:\n%s\n", info_log); free(info_log); } glDeleteShader(shader); return 0; } return shader; } static GLuint CreateProgram(const char *vertex_shader_src, const char *fragment_shader_src) { GLuint vertex_shader = CreateShader(GL_VERTEX_SHADER, vertex_shader_src); if(!vertex_shader) return 0; GLuint fragment_shader = CreateShader(GL_FRAGMENT_SHADER, fragment_shader_src); if(!fragment_shader) { glDeleteShader(vertex_shader); return 0; } GLuint program_object = glCreateProgram(); if(!program_object) return 0; glAttachShader(program_object, vertex_shader); glAttachShader(program_object, fragment_shader); // Link the program glLinkProgram(program_object); // Check the link status GLint linked = 0; glGetProgramiv(program_object, GL_LINK_STATUS, &linked); if(!linked) { GLint info_len = 0; glGetProgramiv(program_object, GL_INFO_LOG_LENGTH, &info_len); if(info_len > 1) { char* info_log = (char *)malloc(info_len); glGetProgramInfoLog(program_object, info_len, NULL, info_log); // TODO(dspringer): We could really use a logging API. printf("Error linking program:\n%s\n", info_log); free(info_log); } glDeleteProgram(program_object); return 0; } // Delete these here because they are attached to the program object. glDeleteShader(vertex_shader); glDeleteShader(fragment_shader); return program_object; } typedef struct ShaderInfo { GLuint program; GLint a_position; GLint a_texcoord; GLint u_vp_matrix; GLint u_texture; } ShaderInfo; static ShaderInfo shader; static ShaderInfo shader_filtering; static GLuint buffers[3]; static GLuint textures[2]; static void gles2_create() { memset(&shader, 0, sizeof(ShaderInfo)); shader.program = CreateProgram(vertex_shader, fragment_shader); if(shader.program) { shader.a_position = glGetAttribLocation(shader.program, "a_position"); shader.a_texcoord = glGetAttribLocation(shader.program, "a_texcoord"); shader.u_vp_matrix = glGetUniformLocation(shader.program, "u_vp_matrix"); shader.u_texture = glGetUniformLocation(shader.program, "u_texture"); } glGenTextures(1, textures); glBindTexture(GL_TEXTURE_2D, textures[0]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, TEX_WIDTH, TEX_HEIGHT, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, NULL); Create_uvs(uvs, (float)frame_width/TEX_WIDTH, (float)frame_height/TEX_HEIGHT); glGenBuffers(3, buffers); glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); glBufferData(GL_ARRAY_BUFFER, kVertexCount * sizeof(GLfloat) * 3, vertices, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, buffers[1]); glBufferData(GL_ARRAY_BUFFER, kVertexCount * sizeof(GLfloat) * 2, uvs, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[2]); glBufferData(GL_ELEMENT_ARRAY_BUFFER, kIndexCount * sizeof(GL_UNSIGNED_SHORT), indices, GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glDisable(GL_DEPTH_TEST); glDisable(GL_BLEND); glDisable(GL_DITHER); } static uint32_t screen_width = 0; static uint32_t screen_height = 0; static EGLDisplay display = NULL; static EGLSurface surface = NULL; static EGLContext context = NULL; static EGL_DISPMANX_WINDOW_T nativewindow; static GLfloat proj[4][4]; static GLint filter_min; static GLint filter_mag; void video_init(uint32_t _width, uint32_t _height, uint32_t filter) { if ((_width==0)||(_height==0)) return; frame_width = _width; frame_height = _height; bcm_host_init(); // get an EGL display connection display = eglGetDisplay(EGL_DEFAULT_DISPLAY); assert(display != EGL_NO_DISPLAY); // initialize the EGL display connection EGLBoolean result = eglInitialize(display, NULL, NULL); assert(EGL_FALSE != result); // get an appropriate EGL frame buffer configuration EGLint num_config; EGLConfig config; static const EGLint attribute_list[] = { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_NONE }; result = eglChooseConfig(display, attribute_list, &config, 1, &num_config); assert(EGL_FALSE != result); result = eglBindAPI(EGL_OPENGL_ES_API); assert(EGL_FALSE != result); // create an EGL rendering context static const EGLint context_attributes[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; context = eglCreateContext(display, config, EGL_NO_CONTEXT, context_attributes); assert(context != EGL_NO_CONTEXT); // create an EGL window surface int32_t success = graphics_get_display_size(0, &screen_width, &screen_height); assert(success >= 0); VC_RECT_T dst_rect; dst_rect.x = 0; dst_rect.y = 0; dst_rect.width = screen_width; dst_rect.height = screen_height; VC_RECT_T src_rect; src_rect.x = 0; src_rect.y = 0; src_rect.width = screen_width << 16; src_rect.height = screen_height << 16; DISPMANX_DISPLAY_HANDLE_T dispman_display = vc_dispmanx_display_open(0); DISPMANX_UPDATE_HANDLE_T dispman_update = vc_dispmanx_update_start(0); DISPMANX_ELEMENT_HANDLE_T dispman_element = vc_dispmanx_element_add(dispman_update, dispman_display, 0, &dst_rect, 0, &src_rect, DISPMANX_PROTECTION_NONE, NULL, NULL, DISPMANX_NO_ROTATE); nativewindow.element = dispman_element; nativewindow.width = screen_width; nativewindow.height = screen_height; vc_dispmanx_update_submit_sync(dispman_update); surface = eglCreateWindowSurface(display, config, &nativewindow, NULL); assert(surface != EGL_NO_SURFACE); // connect the context to the surface result = eglMakeCurrent(display, surface, surface, context); assert(EGL_FALSE != result); gles2_create(); int r=(screen_height*10/frame_height); int h = (frame_height*r)/10; int w = (frame_width*r)/10; glViewport((screen_width-w)/2, (screen_height-h)/2, w, h); SetOrtho(proj, -0.5f, +0.5f, +0.5f, -0.5f, -1.0f, 1.0f, 1.0f ,1.0f ); if (filter==0) { filter_min = GL_NEAREST; filter_mag = GL_NEAREST; } else if (filter==1) { filter_min = GL_LINEAR_MIPMAP_LINEAR; filter_mag = GL_LINEAR; } else if (filter==2) { filter_min = GL_LINEAR_MIPMAP_NEAREST; filter_mag = GL_LINEAR; } } static void gles2_destroy() { if(!shader.program) return; glDeleteBuffers(3, buffers); SHOW_ERROR glDeleteProgram(shader.program); SHOW_ERROR } static void SetOrtho(GLfloat m[4][4], GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat near, GLfloat far, GLfloat scale_x, GLfloat scale_y) { memset(m, 0, 4*4*sizeof(GLfloat)); m[0][0] = 2.0f/(right - left)*scale_x; m[1][1] = 2.0f/(top - bottom)*scale_y; m[2][2] = -2.0f/(far - near); m[3][0] = -(right + left)/(right - left); m[3][1] = -(top + bottom)/(top - bottom); m[3][2] = -(far + near)/(far - near); m[3][3] = 1; } #define RGB15(r, g, b) (((r) << (5+6)) | ((g) << 6) | (b)) static void gles2_Draw( uint16_t *pixels) { if(!shader.program) return; glClear(GL_COLOR_BUFFER_BIT); glUseProgram(shader.program); glBindTexture(GL_TEXTURE_2D, textures[0]); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, frame_width, frame_height, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, pixels); glActiveTexture(GL_TEXTURE0); glUniform1i(shader.u_texture, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter_min); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter_mag); glGenerateMipmap(GL_TEXTURE_2D); glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); glVertexAttribPointer(shader.a_position, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), NULL); glEnableVertexAttribArray(shader.a_position); glBindBuffer(GL_ARRAY_BUFFER, buffers[1]); glVertexAttribPointer(shader.a_texcoord, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), NULL); glEnableVertexAttribArray(shader.a_texcoord); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[2]); glUniformMatrix4fv(shader.u_vp_matrix, 1, GL_FALSE, (const GLfloat * )&proj); glDrawElements(GL_TRIANGLES, kIndexCount, GL_UNSIGNED_SHORT, 0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); //glFlush(); } void video_close() { gles2_destroy(); // Release OpenGL resources eglMakeCurrent( display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT ); eglDestroySurface( display, surface ); eglDestroyContext( display, context ); eglTerminate( display ); } void video_draw(uint16_t *pixels) { gles2_Draw (pixels); eglSwapBuffers(display, surface); }