394 lines
11 KiB
C
394 lines
11 KiB
C
#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);
|
|
}
|