Add copyright message to gles_video
[gpsp.git] / raspberrypi / test / gles_video.c
1 /*
2
3 This file is based on Portable ZX-Spectrum emulator.
4 Copyright (C) 2001-2012 SMT, Dexus, Alone Coder, deathsoft, djdron, scor
5         
6 C++ to C code conversion by Pate
7
8 Modified by DPR for gpsp for Raspberry Pi
9
10 This program is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 3 of the License, or
13 (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program.  If not, see <http://www.gnu.org/licenses/>.
22
23 */
24
25 #include "bcm_host.h"
26 #include "GLES/gl.h"
27 #include "EGL/egl.h"
28 #include "EGL/eglext.h"
29 #include "GLES2/gl2.h"
30 #include <assert.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <memory.h>
34
35 static uint32_t frame_width = 0;
36 static uint32_t frame_height = 0;
37
38
39 #define SHOW_ERROR              gles_show_error();
40
41 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);
42
43 static const char* vertex_shader =
44         "uniform mat4 u_vp_matrix;                                                              \n"
45         "attribute vec4 a_position;                                                             \n"
46         "attribute vec2 a_texcoord;                                                             \n"
47         "varying mediump vec2 v_texcoord;                                               \n"
48         "void main()                                                                                    \n"
49         "{                                                                                                              \n"
50         "       v_texcoord = a_texcoord;                                                        \n"
51         "       gl_Position = u_vp_matrix * a_position;                         \n"
52         "}                                                                                                              \n";
53
54 static const char* fragment_shader =
55         "varying mediump vec2 v_texcoord;                                               \n"
56         "uniform sampler2D u_texture;                                                   \n"
57         "void main()                                                                                    \n"
58         "{                                                                                                              \n"
59         "       gl_FragColor = texture2D(u_texture, v_texcoord);        \n"
60         "}                                                                                                              \n";
61 /*
62 static const GLfloat vertices[] =
63 {
64         -0.5f, -0.5f, 0.0f,
65         +0.5f, -0.5f, 0.0f,
66         +0.5f, +0.5f, 0.0f,
67         -0.5f, +0.5f, 0.0f,
68 };
69 */
70 static const GLfloat vertices[] =
71 {
72         -0.5f, -0.5f, 0.0f,
73         -0.5f, +0.5f, 0.0f,
74         +0.5f, +0.5f, 0.0f,
75         +0.5f, -0.5f, 0.0f,
76 };
77
78 #define TEX_WIDTH       1024
79 #define TEX_HEIGHT      512
80
81 static const GLfloat uvs[8];
82
83 static const GLushort indices[] =
84 {
85         0, 1, 2,
86         0, 2, 3,
87 };
88
89 static const int kVertexCount = 4;
90 static const int kIndexCount = 6;
91
92
93 void Create_uvs(GLfloat * matrix, GLfloat max_u, GLfloat max_v) {
94     memset(matrix,0,sizeof(GLfloat)*8);
95     matrix[3]=max_v;
96     matrix[4]=max_u;
97     matrix[5]=max_v;
98     matrix[6]=max_u;
99
100 }
101
102 void gles_show_error()
103 {
104         GLenum error = GL_NO_ERROR;
105     error = glGetError();
106     if (GL_NO_ERROR != error)
107         printf("GL Error %x encountered!\n", error);
108 }
109
110 static GLuint CreateShader(GLenum type, const char *shader_src)
111 {
112         GLuint shader = glCreateShader(type);
113         if(!shader)
114                 return 0;
115
116         // Load and compile the shader source
117         glShaderSource(shader, 1, &shader_src, NULL);
118         glCompileShader(shader);
119
120         // Check the compile status
121         GLint compiled = 0;
122         glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
123         if(!compiled)
124         {
125                 GLint info_len = 0;
126                 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_len);
127                 if(info_len > 1)
128                 {
129                         char* info_log = (char *)malloc(sizeof(char) * info_len);
130                         glGetShaderInfoLog(shader, info_len, NULL, info_log);
131                         // TODO(dspringer): We could really use a logging API.
132                         printf("Error compiling shader:\n%s\n", info_log);
133                         free(info_log);
134                 }
135                 glDeleteShader(shader);
136                 return 0;
137         }
138         return shader;
139 }
140
141 static GLuint CreateProgram(const char *vertex_shader_src, const char *fragment_shader_src)
142 {
143         GLuint vertex_shader = CreateShader(GL_VERTEX_SHADER, vertex_shader_src);
144         if(!vertex_shader)
145                 return 0;
146         GLuint fragment_shader = CreateShader(GL_FRAGMENT_SHADER, fragment_shader_src);
147         if(!fragment_shader)
148         {
149                 glDeleteShader(vertex_shader);
150                 return 0;
151         }
152
153         GLuint program_object = glCreateProgram();
154         if(!program_object)
155                 return 0;
156         glAttachShader(program_object, vertex_shader);
157         glAttachShader(program_object, fragment_shader);
158
159         // Link the program
160         glLinkProgram(program_object);
161
162         // Check the link status
163         GLint linked = 0;
164         glGetProgramiv(program_object, GL_LINK_STATUS, &linked);
165         if(!linked)
166         {
167                 GLint info_len = 0;
168                 glGetProgramiv(program_object, GL_INFO_LOG_LENGTH, &info_len);
169                 if(info_len > 1)
170                 {
171                         char* info_log = (char *)malloc(info_len);
172                         glGetProgramInfoLog(program_object, info_len, NULL, info_log);
173                         // TODO(dspringer): We could really use a logging API.
174                         printf("Error linking program:\n%s\n", info_log);
175                         free(info_log);
176                 }
177                 glDeleteProgram(program_object);
178                 return 0;
179         }
180         // Delete these here because they are attached to the program object.
181         glDeleteShader(vertex_shader);
182         glDeleteShader(fragment_shader);
183         return program_object;
184 }
185
186 typedef struct ShaderInfo {
187                 GLuint program;
188                 GLint a_position;
189                 GLint a_texcoord;
190                 GLint u_vp_matrix;
191                 GLint u_texture;
192 } ShaderInfo;
193
194 static ShaderInfo shader;
195 static ShaderInfo shader_filtering;
196 static GLuint buffers[3];
197 static GLuint textures[2];
198
199
200 static void gles2_create()
201 {
202         memset(&shader, 0, sizeof(ShaderInfo));
203         shader.program = CreateProgram(vertex_shader, fragment_shader);
204         if(shader.program)
205         {
206                 shader.a_position       = glGetAttribLocation(shader.program,   "a_position");
207                 shader.a_texcoord       = glGetAttribLocation(shader.program,   "a_texcoord");
208                 shader.u_vp_matrix      = glGetUniformLocation(shader.program,  "u_vp_matrix");
209                 shader.u_texture        = glGetUniformLocation(shader.program,  "u_texture");
210         }
211         glGenTextures(1, textures);
212         glBindTexture(GL_TEXTURE_2D, textures[0]);
213         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, TEX_WIDTH, TEX_HEIGHT, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, NULL);
214
215         Create_uvs(uvs, (float)frame_width/TEX_WIDTH, (float)frame_height/TEX_HEIGHT);
216
217         glGenBuffers(3, buffers);
218         glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
219         glBufferData(GL_ARRAY_BUFFER, kVertexCount * sizeof(GLfloat) * 3, vertices, GL_STATIC_DRAW);
220         glBindBuffer(GL_ARRAY_BUFFER, buffers[1]);
221         glBufferData(GL_ARRAY_BUFFER, kVertexCount * sizeof(GLfloat) * 2, uvs, GL_STATIC_DRAW);
222         glBindBuffer(GL_ARRAY_BUFFER, 0);
223         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[2]);
224         glBufferData(GL_ELEMENT_ARRAY_BUFFER, kIndexCount * sizeof(GL_UNSIGNED_SHORT), indices, GL_STATIC_DRAW);
225         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
226
227         glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
228         glDisable(GL_DEPTH_TEST);
229         glDisable(GL_BLEND);
230         glDisable(GL_DITHER);
231 }
232
233 static uint32_t screen_width = 0;
234 static uint32_t screen_height = 0;
235
236 static EGLDisplay display = NULL;
237 static EGLSurface surface = NULL;
238 static EGLContext context = NULL;
239 static EGL_DISPMANX_WINDOW_T nativewindow;
240
241 static GLfloat proj[4][4];
242 static GLint filter_min;
243 static GLint filter_mag;
244
245 void video_set_filter(uint32_t filter) {
246         if (filter==0) {
247             filter_min = GL_NEAREST;
248             filter_mag = GL_NEAREST;
249         } else  {
250             filter_min = GL_LINEAR;
251             filter_mag = GL_LINEAR;
252         }
253 }
254
255 void video_init(uint32_t _width, uint32_t _height, uint32_t filter)
256 {
257         if ((_width==0)||(_height==0))
258                 return;
259
260         frame_width = _width;
261         frame_height = _height;
262         
263         //bcm_host_init();
264
265         // get an EGL display connection
266         display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
267         assert(display != EGL_NO_DISPLAY);
268
269         // initialize the EGL display connection
270         EGLBoolean result = eglInitialize(display, NULL, NULL);
271         assert(EGL_FALSE != result);
272
273         // get an appropriate EGL frame buffer configuration
274         EGLint num_config;
275         EGLConfig config;
276         static const EGLint attribute_list[] =
277         {
278                 EGL_RED_SIZE, 8,
279                 EGL_GREEN_SIZE, 8,
280                 EGL_BLUE_SIZE, 8,
281                 EGL_ALPHA_SIZE, 8,
282                 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
283                 EGL_NONE
284         };
285         result = eglChooseConfig(display, attribute_list, &config, 1, &num_config);
286         assert(EGL_FALSE != result);
287
288         result = eglBindAPI(EGL_OPENGL_ES_API);
289         assert(EGL_FALSE != result);
290
291         // create an EGL rendering context
292         static const EGLint context_attributes[] =
293         {
294                 EGL_CONTEXT_CLIENT_VERSION, 2,
295                 EGL_NONE
296         };
297         context = eglCreateContext(display, config, EGL_NO_CONTEXT, context_attributes);
298         assert(context != EGL_NO_CONTEXT);
299
300         // create an EGL window surface
301         int32_t success = graphics_get_display_size(0, &screen_width, &screen_height);
302         assert(success >= 0);
303
304         VC_RECT_T dst_rect;
305         dst_rect.x = 0;
306         dst_rect.y = 0;
307         dst_rect.width = screen_width;
308         dst_rect.height = screen_height;
309
310         VC_RECT_T src_rect;
311         src_rect.x = 0;
312         src_rect.y = 0;
313         src_rect.width = screen_width << 16;
314         src_rect.height = screen_height << 16;
315
316         DISPMANX_DISPLAY_HANDLE_T dispman_display = vc_dispmanx_display_open(0);
317         DISPMANX_UPDATE_HANDLE_T dispman_update = vc_dispmanx_update_start(0);
318         DISPMANX_ELEMENT_HANDLE_T dispman_element = vc_dispmanx_element_add(dispman_update, dispman_display,
319          1, &dst_rect, 0, &src_rect, DISPMANX_PROTECTION_NONE, NULL, NULL, DISPMANX_NO_ROTATE);
320
321         nativewindow.element = dispman_element;
322         nativewindow.width = screen_width;
323         nativewindow.height = screen_height;
324         vc_dispmanx_update_submit_sync(dispman_update);
325
326         surface = eglCreateWindowSurface(display, config, &nativewindow, NULL);
327         assert(surface != EGL_NO_SURFACE);
328
329         // connect the context to the surface
330         result = eglMakeCurrent(display, surface, surface, context);
331         assert(EGL_FALSE != result);
332
333         gles2_create();
334
335         int r=(screen_height*10/frame_height);
336         int h = (frame_height*r)/10;
337         int w = (frame_width*r)/10;
338         if (w>screen_width) {
339             r = (screen_width*10/frame_width);
340             h = (frame_height*r)/10;
341             w = (frame_width*r)/10;
342         }
343         glViewport((screen_width-w)/2, (screen_height-h)/2, w, h);
344         SetOrtho(proj, -0.5f, +0.5f, +0.5f, -0.5f, -1.0f, 1.0f, 1.0f ,1.0f );
345         video_set_filter(filter);
346 }
347
348 static void gles2_destroy()
349 {
350         if(!shader.program)
351                 return;
352         glDeleteBuffers(3, buffers); SHOW_ERROR
353         glDeleteProgram(shader.program); SHOW_ERROR
354 }
355
356 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)
357 {
358         memset(m, 0, 4*4*sizeof(GLfloat));
359         m[0][0] = 2.0f/(right - left)*scale_x;
360         m[1][1] = 2.0f/(top - bottom)*scale_y;
361         m[2][2] = -2.0f/(far - near);
362         m[3][0] = -(right + left)/(right - left);
363         m[3][1] = -(top + bottom)/(top - bottom);
364         m[3][2] = -(far + near)/(far - near);
365         m[3][3] = 1;
366 }
367 #define RGB15(r, g, b)  (((r) << (5+6)) | ((g) << 6) | (b))
368
369 static void gles2_Draw( uint16_t *pixels)
370 {
371         if(!shader.program)
372                 return;
373
374         glClear(GL_COLOR_BUFFER_BIT);
375
376         glUseProgram(shader.program);
377
378         glBindTexture(GL_TEXTURE_2D, textures[0]);
379         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, frame_width, frame_height, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, pixels);
380         glActiveTexture(GL_TEXTURE0);
381         glUniform1i(shader.u_texture, 0);
382         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter_mag);
383         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter_min);
384         glGenerateMipmap(GL_TEXTURE_2D);
385
386         glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
387         glVertexAttribPointer(shader.a_position, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), NULL);
388         glEnableVertexAttribArray(shader.a_position);
389
390         glBindBuffer(GL_ARRAY_BUFFER, buffers[1]);
391         glVertexAttribPointer(shader.a_texcoord, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), NULL);
392         glEnableVertexAttribArray(shader.a_texcoord);
393
394         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[2]);
395         glUniformMatrix4fv(shader.u_vp_matrix, 1, GL_FALSE, (const GLfloat * )&proj);
396         glDrawElements(GL_TRIANGLES, kIndexCount, GL_UNSIGNED_SHORT, 0);
397
398         glBindBuffer(GL_ARRAY_BUFFER, 0);
399         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
400         //glFlush();
401 }
402
403 void video_close()
404 {
405         gles2_destroy();
406         // Release OpenGL resources
407         eglMakeCurrent( display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );
408         eglDestroySurface( display, surface );
409         eglDestroyContext( display, context );
410         eglTerminate( display );
411 }
412
413 void video_draw(uint16_t *pixels)
414 {
415         gles2_Draw (pixels);
416         eglSwapBuffers(display, surface);
417 }