diff options
-rw-r--r-- | common.h | 4 | ||||
-rw-r--r-- | gui.c | 35 | ||||
-rw-r--r-- | input.c | 122 | ||||
-rw-r--r-- | main.c | 10 | ||||
-rw-r--r-- | raspberrypi/Makefile | 44 | ||||
-rw-r--r-- | raspberrypi/gles_video.c | 393 | ||||
-rw-r--r-- | raspberrypi/gles_video.h | 4 | ||||
-rw-r--r-- | raspberrypi/keys.txt | 33 | ||||
-rw-r--r-- | raspberrypi/rpi.c | 111 | ||||
-rw-r--r-- | raspberrypi/rpi.h | 9 | ||||
-rw-r--r-- | raspberrypi/test/Makefile | 30 | ||||
-rw-r--r-- | raspberrypi/test/gles_video.c | 383 | ||||
-rw-r--r-- | raspberrypi/test/test.c | 48 | ||||
-rw-r--r-- | sound.c | 4 | ||||
-rw-r--r-- | video.c | 16 |
15 files changed, 1216 insertions, 30 deletions
@@ -270,4 +270,8 @@ typedef u32 fixed8_24; #include "pandora/pnd.h" #endif +#ifdef RPI_BUILD + #include "raspberrypi/rpi.h" +#endif + #endif @@ -552,7 +552,7 @@ typedef enum NUMBER_SELECTION_OPTION = 0x01, STRING_SELECTION_OPTION = 0x02, SUBMENU_OPTION = 0x04, - ACTION_OPTION = 0x08 + ACTION_OPTION = 0x08, } menu_option_type_enum; struct _menu_type @@ -746,6 +746,13 @@ u32 gamepad_config_line_to_button[] = #endif +#ifdef RPI_BUILD + +u32 gamepad_config_line_to_button[] = + { 0, 2, 1, 3, 8, 9, 10, 11, 6, 7, 4, 5, 12, 13, 14, 15 }; + +#endif + static const char *scale_options[] = { #ifdef PSP_BUILD @@ -759,6 +766,8 @@ static const char *scale_options[] = "unscaled", "2x", "3x", "fullscreen" #elif defined(GP2X_BUILD) "unscaled 3:2", "scaled 3:2", "fullscreen", "scaled 3:2 (software)" +#elif defined(RPI_BUILD) + "fullscreen" #else "unscaled 3:2" #endif @@ -843,11 +852,16 @@ s32 load_game_config_file() if(file_loaded) return 0; +#ifdef RPI_BUILD + current_frameskip_type = manual_frameskip; + frameskip_value = 1; +#else current_frameskip_type = auto_frameskip; frameskip_value = 4; #ifdef POLLUX_BUILD frameskip_value = 1; #endif +#endif random_skip = 0; clock_speed = default_clock_speed; @@ -1213,10 +1227,6 @@ u32 menu(u16 *original_screen) reg[CHANGED_PC_STATUS] = 1; menu_update_clock(); } - else - { - choose_menu(current_menu); - } } void menu_restart() @@ -1329,8 +1339,9 @@ u32 menu(u16 *original_screen) static const char *update_backup_options[] = { "Exit only", "Automatic" }; // Marker for help information, don't go past this mark (except \n)------* - menu_option_type graphics_sound_options[] = - { + menu_option_type graphics_sound_options[] = + { +#ifndef RPI_BUILD string_selection_option(NULL, "Display scaling", scale_options, (u32 *)(&screen_scale), sizeof(scale_options) / sizeof(scale_options[0]), @@ -1344,6 +1355,8 @@ u32 menu(u16 *original_screen) #endif #endif "", 2), +#endif + #ifndef GP2X_BUILD string_selection_option(NULL, "Screen filtering", yes_no_options, (u32 *)(&screen_filter), 2, @@ -1352,7 +1365,7 @@ u32 menu(u16 *original_screen) "smooth image, at the cost of being blurry and having less vibrant\n" "colors.", 3), #endif -#ifdef PND_BUILD +#if defined (PND_BUILD) string_selection_option(NULL, "Scaling filter", filter2_options, (u32 *)(&screen_filter2), sizeof(filter2_options) / sizeof(filter2_options[0]), @@ -1572,7 +1585,7 @@ u32 menu(u16 *original_screen) #endif -#ifdef PC_BUILD +#if defined(PC_BUILD) || defined(RPI_BUILD) menu_option_type gamepad_config_options[] = { @@ -1801,6 +1814,10 @@ u32 menu(u16 *original_screen) if(current_option->option_type & SUBMENU_OPTION) choose_menu(current_option->sub_menu); + + if(current_menu == &main_menu) + choose_menu(&main_menu); + break; default: @@ -555,8 +555,49 @@ void init_input() #endif +#if defined(RPI_BUILD) -#ifdef PC_BUILD +u32 key_map(SDLKey key_sym) +{ + switch(key_sym) + { + case SDLK_a: + return BUTTON_L; + + case SDLK_s: + return BUTTON_R; + + case SDLK_DOWN: + return BUTTON_DOWN; + + case SDLK_UP: + return BUTTON_UP; + + case SDLK_LEFT: + return BUTTON_LEFT; + + case SDLK_RIGHT: + return BUTTON_RIGHT; + + case SDLK_RETURN: + return BUTTON_START; + + case SDLK_BACKSPACE: + return BUTTON_SELECT; + + case SDLK_x: + return BUTTON_B; + + case SDLK_z: + return BUTTON_A; + + default: + return BUTTON_NONE; + } +} +#endif + +#if defined(PC_BUILD) u32 key_map(SDLKey key_sym) { @@ -596,6 +637,8 @@ u32 key_map(SDLKey key_sym) return BUTTON_NONE; } } +#endif +#if defined(PC_BUILD) || defined(RPI_BUILD) u32 joy_map(u32 button) { @@ -607,16 +650,16 @@ u32 joy_map(u32 button) case 5: return BUTTON_R; - case 9: + case 2: return BUTTON_START; - case 8: + case 3: return BUTTON_SELECT; - case 0: + case 1: return BUTTON_B; - case 1: + case 0: return BUTTON_A; default: @@ -632,7 +675,7 @@ gui_action_type get_gui_input() delay_us(30000); while(SDL_PollEvent(&event)) - { + { switch(event.type) { case SDL_QUIT: @@ -669,15 +712,49 @@ gui_action_type get_gui_input() case SDLK_BACKSPACE: gui_action = CURSOR_BACK; break; + default: + break; + } + } + break; +#ifdef RPI_BUILD + case SDL_JOYBUTTONDOWN: + { + switch (event.jbutton.button) + { + case 2: + gui_action = CURSOR_BACK; + break; - default: + case 1: + gui_action = CURSOR_EXIT; break; - } + + case 0: + gui_action = CURSOR_SELECT; + break; + } + } + break; + + case SDL_JOYAXISMOTION: + { + if (event.jaxis.axis==0) { //Left-Right + if (event.jaxis.value < -3200) gui_action = CURSOR_LEFT; + else if (event.jaxis.value > 3200) gui_action = CURSOR_RIGHT; + } + if (event.jaxis.axis==1) { //Up-Down + if (event.jaxis.value < -3200) gui_action = CURSOR_UP; + else if (event.jaxis.value > 3200) gui_action = CURSOR_DOWN; + } + } + break; + +#endif + default: break; - } } } - return gui_action; } @@ -698,8 +775,11 @@ u32 update_input() { quit(); } - +#ifdef PC_BUILD if(event.key.keysym.sym == SDLK_BACKSPACE) +#else + if(event.key.keysym.sym == SDLK_F10) +#endif { u16 *screen_copy = copy_screen(); u32 ret_val = menu(screen_copy); @@ -708,7 +788,7 @@ u32 update_input() return ret_val; } else - +#ifdef PC_BUILD if(event.key.keysym.sym == SDLK_F1) { debug_on(); @@ -741,7 +821,7 @@ u32 update_input() dump_translation_cache(); } else - +#endif if(event.key.keysym.sym == SDLK_F5) { char current_savestate_filename[512]; @@ -795,6 +875,22 @@ u32 update_input() key &= ~(joy_map(event.jbutton.button)); break; } +#ifdef RPI_BUILD + case SDL_JOYAXISMOTION: + { + if (event.jaxis.axis==0) { //Left-Right + key &= ~(BUTTON_LEFT|BUTTON_RIGHT); + if (event.jaxis.value < -3200) key |= BUTTON_LEFT; + else if (event.jaxis.value > 3200) key |= BUTTON_RIGHT; + } + if (event.jaxis.axis==1) { //Up-Down + key &= ~(BUTTON_UP|BUTTON_DOWN); + if (event.jaxis.value < -3200) key |= BUTTON_UP; + else if (event.jaxis.value > 3200) key |= BUTTON_DOWN; + } + break; +#endif + } } } @@ -38,8 +38,14 @@ debug_state current_debug_state = RUN; //u32 breakpoint_value = 0; +#ifdef RPI_BUILD +frameskip_type current_frameskip_type = manual_frameskip; //manual; //auto_frameskip; +u32 global_cycles_per_instruction = 1; +#else frameskip_type current_frameskip_type = auto_frameskip; u32 global_cycles_per_instruction = 1; +#endif + u32 random_skip = 0; u32 fps_debug = 0; @@ -821,7 +827,7 @@ void synchronize() } else if (synchronize_flag) { -#if defined(PND_BUILD) +#if defined(PND_BUILD) || defined(RPI_BUILD) fb_wait_vsync(); #elif !defined(GP2X_BUILD) // sleeping on GP2X is a bad idea delay_us((u64)virtual_frame_count * 50000 / 3 - new_ticks + 2); @@ -867,7 +873,7 @@ void synchronize() interval_skipped_frames += skip_next_frame; -#if !defined(GP2X_BUILD) && !defined(PND_BUILD) +#if !defined(GP2X_BUILD) && !defined(PND_BUILD) && !defined(RPI_BUILD) char char_buffer[64]; sprintf(char_buffer, "gpSP: %2d (%2d) fps", fps, frames_drawn); SDL_WM_SetCaption(char_buffer, "gpSP"); diff --git a/raspberrypi/Makefile b/raspberrypi/Makefile new file mode 100644 index 0000000..336a835 --- /dev/null +++ b/raspberrypi/Makefile @@ -0,0 +1,44 @@ +# gpSP makefile +# Gilead Kutnick - Exophase +# pandora port - notaz +# respberry pi - DPR + +# Global definitions + +CC = gcc + +OBJS = rpi.o main.o cpu.o memory.o video.o input.o sound.o gui.o \ + cheats.o zip.o arm_stub.o warm.o cpu_threaded.o\ + gles_video.o video_blend.o + +BIN = gpsp + +# Platform specific definitions + +VPATH += .. ../arm +CFLAGS += -DARM_ARCH -DRPI_BUILD -Wall +CFLAGS += -O3 -mfpu=vfp +CFLAGS += `sdl-config --cflags` +CFLAGS += -I$(SDKSTAGE)/opt/vc/include -I$(SDKSTAGE)/opt/vc/include/interface/vcos/pthreads + +# expecting to have PATH set up to get correct sdl-config first + +LIBS += `sdl-config --libs` +LIBS += -ldl -lpthread -lz +LIBS += -L$(SDKSTAGE)/opt/vc/lib/ -lGLESv2 -lEGL -lopenmaxil -lbcm_host -lvcos -lvchiq_arm -lrt + +# Compilation: + +all: $(BIN) + +%.o: %.S + $(CC) $(CFLAGS) -c -o $@ $< + + +cpu.o cpu_threaded.o: CFLAGS += -Wno-unused-variable -Wno-unused-label + +$(BIN): $(OBJS) + $(CC) $(OBJS) $(LIBS) -o $(BIN) + +clean: + rm -f *.o $(BIN) diff --git a/raspberrypi/gles_video.c b/raspberrypi/gles_video.c new file mode 100644 index 0000000..1623bdc --- /dev/null +++ b/raspberrypi/gles_video.c @@ -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); +} diff --git a/raspberrypi/gles_video.h b/raspberrypi/gles_video.h new file mode 100644 index 0000000..cd87f92 --- /dev/null +++ b/raspberrypi/gles_video.h @@ -0,0 +1,4 @@ + +void video_init(uint32_t width,uint32_t height,uint32_t filter); +void video_close(); +void video_draw(uint16_t *pixels); diff --git a/raspberrypi/keys.txt b/raspberrypi/keys.txt new file mode 100644 index 0000000..4bfbdd5 --- /dev/null +++ b/raspberrypi/keys.txt @@ -0,0 +1,33 @@ +gpsp raspberry pi + +CONTROL KEYS +============ +KEYBOARD +--------- +Up Up Arrow +Down Down Arrow +Left Left Arrow +Right Right Arrow +A Z +B X +Start Enter +Select Backspace +L A +R S + +Exit Esc +Menu F10 + +GAMEPAD +------- +Up XAsix - +Down XAsix + +Left YAsix - +Right YAsix + +A Button 1 +B Button 2 +Start Button 3 +Select Button 4 +L Button 5 +R Button 6 + diff --git a/raspberrypi/rpi.c b/raspberrypi/rpi.c new file mode 100644 index 0000000..3fbecb8 --- /dev/null +++ b/raspberrypi/rpi.c @@ -0,0 +1,111 @@ +/* gameplaySP - raspberry backend + * + * Copyright (C) 2013 DPR <pribyl.email@gmail.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "../common.h" +#include <sys/types.h> +#include <sys/stat.h> +#include "gles_video.h" +#include "rpi.h" +#include "bcm_host.h" + +u32 gamepad_config_map[PLAT_BUTTON_COUNT] = +{ + BUTTON_ID_UP, // Up + BUTTON_ID_LEFT, // Left + BUTTON_ID_DOWN, // Down + BUTTON_ID_RIGHT, // Right + BUTTON_ID_START, // Start + BUTTON_ID_SELECT, // Select + BUTTON_ID_L, // Ltrigger + BUTTON_ID_R, // Rtrigger + BUTTON_ID_FPS, // A + BUTTON_ID_A, // B + BUTTON_ID_B, // X + BUTTON_ID_MENU, // Y + BUTTON_ID_SAVESTATE, // 1 + BUTTON_ID_LOADSTATE, // 2 + BUTTON_ID_FASTFORWARD, // 3 + BUTTON_ID_NONE, // 4 + BUTTON_ID_MENU // Space +}; + + +#define MAX_VIDEO_MEM (480*270*2) +static int video_started=0; +static uint16_t * video_buff; + + +void gpsp_plat_init(void) +{ + int ret, w, h, fd; + //const char *layer_fb_name; + SDL_Surface* myVideoSurface; + + bcm_host_init(); + + ret = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK | SDL_INIT_NOPARACHUTE); + if (ret != 0) { + fprintf(stderr, "SDL_Init failed: %s\n", SDL_GetError()); + exit(1); + } + + myVideoSurface = SDL_SetVideoMode( 0, 0, 16, SDL_SWSURFACE); + // Print out some information about the video surface + if (myVideoSurface == NULL) { + fprintf(stderr, "SDL_Init failed: %s\n", SDL_GetError()); + exit(1); + } + SDL_ShowCursor(0); + fb_set_mode(240, 160, 0, 0, 0, 0); + screen_scale = 3; +} + +void gpsp_plat_quit(void) +{ + if (video_started) { + video_close(); + free(video_buff); + video_started=0; + } + SDL_Quit(); +} + + +void *fb_flip_screen(void) +{ + video_draw(video_buff); + return video_buff; +} + +void fb_wait_vsync(void) +{ +} + +void fb_set_mode(int w, int h, int buffers, int scale,int filter, int filter2) +{ + if (video_started) { + video_close(); + free(video_buff); + } + video_buff=malloc(w*h*sizeof(uint16_t)); + memset(video_buff,0,w*h*sizeof(uint16_t)); + video_init(w,h,filter); + video_started=1; +} +// vim:shiftwidth=2:expandtab diff --git a/raspberrypi/rpi.h b/raspberrypi/rpi.h new file mode 100644 index 0000000..536414d --- /dev/null +++ b/raspberrypi/rpi.h @@ -0,0 +1,9 @@ +void gpsp_plat_init(void); +void gpsp_plat_quit(void); + +#define PLAT_BUTTON_COUNT 17 +extern u32 button_plat_mask_to_config[PLAT_BUTTON_COUNT]; + +void *fb_flip_screen(void); +void fb_set_mode(int w, int h, int buffers, int scale, int filter, int filter2); +void fb_wait_vsync(void); diff --git a/raspberrypi/test/Makefile b/raspberrypi/test/Makefile new file mode 100644 index 0000000..9830834 --- /dev/null +++ b/raspberrypi/test/Makefile @@ -0,0 +1,30 @@ +# glestest makefile +# Global definitions + +CC = gcc + +OBJS = gles_video.o test.o + +BIN = glestest + +# Platform specific definitions + +CFLAGS+=-D_LINUX + +LIBS +=-L$(SDKSTAGE)/opt/vc/lib/ -lGLESv2 -lEGL -lopenmaxil -lbcm_host -lvcos -lvchiq_arm -lpthread -lrt + +INCLUDES+=-I$(SDKSTAGE)/opt/vc/include -I$(SDKSTAGE)/opt/vc/include/interface/vcos/pthreads + +# Compilation: + +all: $(BIN) + +%.o: %.c + $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $< + +$(BIN): $(OBJS) + $(CC) $(OBJS) $(LIBS) -o $(BIN) + +clean: + rm -f *.o $(BIN) + diff --git a/raspberrypi/test/gles_video.c b/raspberrypi/test/gles_video.c new file mode 100644 index 0000000..4d7d405 --- /dev/null +++ b/raspberrypi/test/gles_video.c @@ -0,0 +1,383 @@ +#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, +}; + +#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); +} diff --git a/raspberrypi/test/test.c b/raspberrypi/test/test.c new file mode 100644 index 0000000..17e3d13 --- /dev/null +++ b/raspberrypi/test/test.c @@ -0,0 +1,48 @@ +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> + +#define RGB15(r, g, b) (((r) << (5+6)) | ((g) << 6) | (b)) + +void video_init(uint32_t width,uint32_t height, uint32_t f); +void video_close(); +void video_draw(uint16_t *pixels); + + + +void showbitmap( uint32_t wd, uint32_t ht, uint32_t f) { +int index; +uint16_t j,k; +uint8_t r,g,b; +uint16_t * bitmap; + + bitmap=malloc(wd*ht*sizeof(uint16_t)); + + b=16; + index=0; + for (j=0;j<ht;j++) { + r=(j*31)/(ht-1); + for (k=0; k<wd ; k++) { + g=(k*31)/(wd-1); + bitmap[index++]=RGB15(r,g,b); + } + } +bitmap[0]=0; + +video_init(wd,ht,f); +video_draw(bitmap); +sleep(5); +video_close(); +free(bitmap); +} + +int main(void) { + +showbitmap( 320, 240,0); +showbitmap( 320, 240,1); +showbitmap( 320, 240,2); +showbitmap( 240, 160,0); +showbitmap( 400, 272,0); + +return 0; +}
\ No newline at end of file @@ -25,7 +25,11 @@ u32 global_enable_audio = 1; direct_sound_struct direct_sound_channel[2]; gbc_sound_struct gbc_sound_channel[4]; +#ifdef RPI_BUILD +u32 sound_frequency = 22050; +#else u32 sound_frequency = 44100; +#endif SDL_mutex *sound_mutex; static SDL_cond *sound_cv; @@ -105,7 +105,7 @@ const u32 screen_pitch = 320; #define get_screen_pitch() \ screen_pitch \ -#elif defined(PND_BUILD) +#elif defined(PND_BUILD) || defined(RPI_BUILD) static u16 *screen_pixels = NULL; @@ -3388,7 +3388,7 @@ no_clean: screen_pixels = (u16 *)gpsp_gp2x_screen + screen_offset; } -#elif defined(PND_BUILD) +#elif defined(PND_BUILD) || defined(RPI_BUILD) void flip_screen() { @@ -3604,7 +3604,7 @@ void init_video() GE_CMD(NOP, 0); } -#elif defined(POLLUX_BUILD) || defined(PND_BUILD) +#elif defined(WIZ_BUILD) || defined(PND_BUILD) || defined (RPI_BUILD) void init_video() { @@ -3800,14 +3800,18 @@ void clear_screen(u16 color) *p++ = col; } -#elif defined(PND_BUILD) +#elif defined(PND_BUILD) || defined(RPI_BUILD) void video_resolution_large() { +#if defined (RPI_BUILD) + resolution_width = 480; +#else resolution_width = 400; +#endif resolution_height = 272; - fb_set_mode(400, 272, 1, 15, screen_filter, screen_filter2); + fb_set_mode(resolution_width, resolution_height, 1, 15, screen_filter, screen_filter2); flip_screen(); clear_screen(0); } @@ -3817,7 +3821,7 @@ void video_resolution_small() resolution_width = 240; resolution_height = 160; - fb_set_mode(240, 160, 3, screen_scale, screen_filter, screen_filter2); + fb_set_mode(resolution_width, resolution_height, 3, screen_scale, screen_filter, screen_filter2); flip_screen(); clear_screen(0); } |