raspberry pi port
[gpsp.git] / raspberrypi / gles_video.c
diff --git a/raspberrypi/gles_video.c b/raspberrypi/gles_video.c
new file mode 100644 (file)
index 0000000..1623bdc
--- /dev/null
@@ -0,0 +1,393 @@
+#include "bcm_host.h"
+#include "GLES/gl.h"
+#include "EGL/egl.h"
+#include "EGL/eglext.h"
+#include "GLES2/gl2.h"
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <memory.h>
+
+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,
+};
+*/
+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_set_filter(uint32_t filter) {
+       if (filter==0) {
+           filter_min = GL_NEAREST;
+           filter_mag = GL_NEAREST;
+       } else  {
+           filter_min = GL_LINEAR;
+           filter_mag = GL_LINEAR;
+       }
+}
+
+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,
+        1, &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;
+       if (w>screen_width) {
+           r = (screen_width*10/frame_width);
+           h = (frame_height*r)/10;
+           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 );
+       video_set_filter(filter);
+}
+
+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_MAG_FILTER, filter_mag);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter_min);
+       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);
+}