Commit | Line | Data |
---|---|---|
862e940b D |
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 | ||
ffa573f8 D |
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 | } |