/** * This function is called at the end of the frame rendering function */ static void handle_quit_sequence() { int fade_time = 750 * 1000; int64_t dt = fs_emu_monotonic_time() - g_fs_emu_quit_time; if (dt > fade_time && g_fs_emu_emulation_thread_stopped) { fs_emu_log("calling fs_ml_stop because emu thread is done\n"); fs_ml_stop(); } else if (dt > 5 * 1000 * 1000) { // 5 seconds has passed after shutdown was requested fs_emu_log("calling fs_ml_stop because emu does not stop\n"); // FIXME: FORCE STOP fs_ml_stop(); fs_emu_log("force-closing the emulator\n"); exit(1); } // fade out over 750ms float fade = (1.0 * dt) / fade_time; if (fade > 1.0) { fade = 1.0; } // draw fading effect fs_gl_viewport(0, 0, fs_ml_video_width(), fs_ml_video_height()); fs_gl_ortho_hd(); fs_gl_blending(1); fs_gl_texturing(0); fs_gl_color4f(0.0, 0.0, 0.0, fade); GLfloat vert[] = { 0, 0, 1920, 0, 1920, 1080, 0, 1080 }; glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(2, GL_FLOAT, 0, vert); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDisableClientState(GL_VERTEX_ARRAY); CHECK_GL_ERROR(); }
static void save_screenshot_of_opengl_framebuffer(const char *path) { #if 0 static int count = 0; count += 1; time_t t = time(NULL); #ifdef WINDOWS struct tm *tm_p = localtime(&t); #else struct tm tm_struct; struct tm *tm_p = &tm_struct; localtime_r(&t, tm_p); #endif char strbuf[20]; strftime(strbuf, 20, "%Y-%m-%d-%H-%M", tm_p); char *name = g_strdup_printf("%s-%s-%03d.png", g_fs_ml_video_screenshots_prefix, strbuf, g_fs_ml_video_screenshot); char *path = g_build_filename(g_fs_ml_video_screenshots_dir, name, NULL); #endif fs_log("writing screenshot to %s\n", path); int w = fs_ml_video_width(); int h = fs_ml_video_height(); fs_log("reading opengl frame buffer (%d x %d)\n", w, h); void *out_data = g_malloc(w * h * 4); // when using GL_RGB, remeber to temporarily set GL_UNPACK_ALIGNMENT so // all rows will be contiguous (the OpenGL default is to align rows on // 4-byte boundaries //GLint unpack_alignment; //glGetIntegerv(GL_UNPACK_ALIGNMENT, &unpack_alignment); //glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, out_data); //glPixelStorei(GL_UNPACK_ALIGNMENT, unpack_alignment); // flip image vertically int stride = w * 4; void *tmp = g_malloc(stride); void *line1 = out_data; void *line2 = out_data + stride * (h - 1); for (int i = 0; i < h / 2; i++) { memcpy(tmp, line1, stride); memcpy(line1, line2, stride); memcpy(line2, tmp, stride); line1 += stride; line2 -= stride; } g_free(tmp); int result = fs_image_save_data(path, out_data, w, h, 4); if (result) { fs_log("saved screenshot\n"); } else { fs_log("error saving screenshot\n"); } g_free(out_data); #if 0 g_free(name); g_free(path); #endif }
void fs_emu_video_render_function() { static int initialized_menu = 0; if (!initialized_menu) { // render menu once (without really showing it, so all menu // resources are initialized and loaded, -prevents flickering // when really opening the menu later fs_emu_render_menu(g_menu_transition); initialized_menu = 1; } if (g_fs_emu_video_debug) { int quarter_height = fs_ml_video_height() / 4; fs_gl_viewport(0, quarter_height, fs_ml_video_width(), fs_ml_video_height() - quarter_height); } else { fs_gl_viewport(0, 0, fs_ml_video_width(), fs_ml_video_height()); } // FIXME: can perhaps remove this soon.. fs_emu_video_render_mutex_lock(); int in_menu = fs_emu_menu_is_active(); if (in_menu && g_menu_transition_target < 1.0) { g_menu_transition_target = 1.0; } if (!in_menu && g_menu_transition_target > 0.0) { g_menu_transition_target = 0.0; } // FIXME: ideally, we would use time-based animation - for now, we use a // simple frame-based animation if (g_menu_transition < g_menu_transition_target) { if (g_menu_transition_target == 1.0) { g_menu_transition += 0.10; } } if (g_menu_transition > g_menu_transition_target) { if (g_menu_transition_target == 0.0) { g_menu_transition -= 0.10; } } if (g_menu_transition > 1.0) { g_menu_transition = 1.0; } else if (g_menu_transition < 0.0) { g_menu_transition = 0.0; } int matrix_pushed = 0; double t0_x = 0.0; double t0_y = 0.0; double t0_z = -2.42; double r0_a = 0.0; double t1_x = -0.31; //double t1_y = -0.04; double t1_y = 0.0; double t1_z = -3.7; double r1_a = 30.0; int perspective = 0; if (g_menu_transition == 0.0) { perspective = 0; fs_gl_ortho(); //glTranslated(1920.0 / 2.0, 1080.0 / 2.0, 0.0); //glScaled(1920.0 / 2.0, 1080.0 / 2.0, 1.0); } else { perspective = 1; glClearColor(0.1, 0.1, 0.1, 1.0); glClear(GL_DEPTH_BUFFER_BIT); CHECK_GL_ERROR(); fs_gl_ortho_hd(); // transition y-coordinate between floor and wall int splt = 361; fs_gl_blending(FALSE); fs_gl_texturing(FALSE); GLfloat color[] = { 39.0 / 255.0, 44.0 / 255.0, 51.0 / 255.0, 1.0, 39.0 / 255.0, 44.0 / 255.0, 51.0 / 255.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 20.0 / 255.0, 22.0 / 255.0, 26.0 / 255.0, 1.0, 20.0 / 255.0, 22.0 / 255.0, 26.0 / 255.0, 1.0 }; GLfloat vert[] = { 0, splt, -0.9, 1920, splt, -0.9, 1920, 1020, -0.9, 0, 1020, -0.9, 0, 1020, -0.9, 1920, 1020, -0.9, 1920, 1080, -0.9, 0, 1080, -0.9, 0, 0, -0.9, 1920, 0, -0.9, 1920, splt, -0.9, 0, splt, -0.9 }; glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glColorPointer(4, GL_FLOAT, 0, color); glVertexPointer(3, GL_FLOAT, 0, vert); glDrawArrays(GL_TRIANGLE_FAN, 0, 12); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_COLOR_ARRAY); CHECK_GL_ERROR(); fs_gl_perspective(); double t_x = t0_x + (t1_x - t0_x) * g_menu_transition; double t_y = t0_y + (t1_y - t0_y) * g_menu_transition; double t_z = t0_z + (t1_z - t0_z) * g_menu_transition; double r_a = r0_a + (r1_a - r0_a) * g_menu_transition; glPushMatrix(); matrix_pushed = 1; glScaled(16.0 / 9.0, 1.0, 1.0); glTranslated(t_x, t_y, t_z); glRotated(r_a, 0.0, 1.0, 0.0); CHECK_GL_ERROR(); } if (perspective) { render_glow(g_menu_transition); } if (perspective) { glPushMatrix(); glTranslatef(0.0, -2.0, 0.0); //glTranslatef(0.0, -1.0, 0.0); //glScalef(1.0, -1.0, 1.0); glScalef(1.0, -0.5, 1.0); glTranslatef(0.0, -1.0, 0.0); CHECK_GL_ERROR(); render_frame(0.33, perspective); CHECK_GL_ERROR(); render_gloss(g_menu_transition * 0.66); CHECK_GL_ERROR(); glPopMatrix(); CHECK_GL_ERROR(); } render_frame(1.0, perspective); if (perspective) { render_gloss(g_menu_transition); } /* if (fs_emu_is_paused()) { render_pause_fade(); } */ if (matrix_pushed) { glPopMatrix(); CHECK_GL_ERROR(); matrix_pushed = 0; } fs_emu_acquire_gui_lock(); fs_emu_render_chat(); //if (fs_emu_menu_is_active()) { if (g_menu_transition > 0.0) { fs_emu_render_menu(g_menu_transition); } fs_emu_render_dialog(); fs_emu_release_gui_lock(); if (g_fs_emu_hud_mode && fs_emu_netplay_enabled()) { fs_gl_ortho_hd(); fs_gl_texturing(0); fs_gl_blending(1); fs_gl_color4f(0.0, 0.0, 0.0, 0.5); GLfloat vert[] = { 0, 1030, 1920, 1030, 1920, 1080, 0, 1080 }; glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(2, GL_FLOAT, 0, vert); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDisableClientState(GL_VERTEX_ARRAY); CHECK_GL_ERROR(); #if 0 glBegin(GL_QUADS); glVertex2f(0, 1030); glVertex2f(1920, 1030); fs_gl_color4f(0.0, 0.0, 0.0, 0.0); glVertex2f(1920, 1030 - 50); glVertex2f(0, 1030 - 50); glEnd(); #endif fs_emu_font *menu_font = fs_emu_font_get_menu(); char *str; for (int i = 0; i < MAX_PLAYERS; i++) { fs_emu_player *player = g_fs_emu_players + i; int x = i * 1920 / 6 + 20; int y = 1038; int rendered_tag = 0; if (player->tag && player->tag[0]) { str = g_strdup_printf("%s", player->tag); fs_emu_font_render(menu_font, str, x, y, 1.0, 1.0, 1.0, 1.0); g_free(str); rendered_tag = 1; } if (rendered_tag || player->ping) { str = g_strdup_printf("%03d", player->ping); fs_emu_font_render(menu_font, str, x + 100, y, 1.0, 1.0, 1.0, 1.0); g_free(str); } if (rendered_tag || player->lag) { str = g_strdup_printf("%03d", player->lag); fs_emu_font_render(menu_font, str, x + 200, y, 1.0, 1.0, 1.0, 1.0); g_free(str); } } } if (g_fs_emu_video_debug) { int quarter_height = fs_ml_video_height() / 4; fs_gl_viewport(0, 0, fs_ml_video_width(), quarter_height); CHECK_GL_ERROR(); fs_emu_set_texture(NULL); CHECK_GL_ERROR(); static GLuint debug_texture = 0; static uint32_t *debug_texture_data = NULL; if (debug_texture == 0) { debug_texture_data = g_malloc0(256 * 256 * 4); glGenTextures(1, &debug_texture); CHECK_GL_ERROR(); fs_gl_bind_texture(debug_texture); #ifndef HAVE_GLES fs_gl_unpack_row_length(0); #endif glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); CHECK_GL_ERROR(); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); CHECK_GL_ERROR(); } else { fs_gl_bind_texture(debug_texture); CHECK_GL_ERROR(); } memset(debug_texture_data, 0x00, 256 * 256 * 4); CHECK_GL_ERROR(); fs_emu_video_render_debug_info(debug_texture_data); CHECK_GL_ERROR(); fs_emu_audio_render_debug_info(debug_texture_data); CHECK_GL_ERROR(); #ifndef HAVE_GLES fs_gl_unpack_row_length(0); #endif CHECK_GL_ERROR(); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, debug_texture_data); CHECK_GL_ERROR(); fs_gl_ortho_hd(); fs_gl_texturing(1); fs_gl_blending(0); fs_gl_color4f(1.0, 1.0, 1.0, 1.0); GLfloat tex[] = { 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0 }; GLfloat vert[] = { 0, 0, 1920, 0, 1920, 1080, 0, 1080 }; glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glTexCoordPointer(2, GL_FLOAT, 0, tex); glVertexPointer(2, GL_FLOAT, 0, vert); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); CHECK_GL_ERROR(); glPushMatrix(); glScalef(1.0, 4.0, 1.0); fs_emu_font *menu_font = fs_emu_font_get_menu(); char *str; /* str = g_strdup_printf("%d", fs_emu_get_audio_frequency()); fs_emu_font_render(menu_font, str, 1920 / 2 + 20, 3, 1.0, 1.0, 1.0, 1.0); g_free(str); */ str = g_strdup_printf("%0.1f", fs_emu_audio_get_measured_avg_buffer_fill(0) / 1000.0); fs_emu_font_render(menu_font, str, 1920 / 2 + 220, 3, 1.0, 1.0, 1.0, 1.0); g_free(str); str = g_strdup_printf("%d", g_fs_emu_audio_buffer_underruns); fs_emu_font_render(menu_font, str, 1920 / 2 + 420, 3, 1.0, 1.0, 1.0, 1.0); g_free(str); fs_emu_font_render(menu_font, "EMU", 20, 3, 1.0, 1.0, 1.0, 1.0); str = g_strdup_printf("%0.1f", fs_emu_get_average_emu_fps()); fs_emu_font_render(menu_font, str, 220, 3, 1.0, 1.0, 1.0, 1.0); g_free(str); str = g_strdup_printf("%d", g_fs_emu_lost_frames); fs_emu_font_render(menu_font, str, 420, 3, 1.0, 1.0, 1.0, 1.0); g_free(str); str = g_strdup_printf("%d", g_fs_emu_repeated_frames); fs_emu_font_render(menu_font, str, 620, 3, 1.0, 1.0, 1.0, 1.0); g_free(str); fs_emu_font_render(menu_font, "SYS", 20, 140, 1.0, 1.0, 1.0, 1.0); str = g_strdup_printf("%0.1f", fs_emu_get_average_sys_fps()); fs_emu_font_render(menu_font, str, 220, 140, 1.0, 1.0, 1.0, 1.0); g_free(str); str = g_strdup_printf("%d", g_fs_emu_lost_vblanks); fs_emu_font_render(menu_font, str, 420, 140, 1.0, 1.0, 1.0, 1.0); g_free(str); glPopMatrix(); CHECK_GL_ERROR(); } if (fs_emu_is_quitting()) { handle_quit_sequence(); } fs_emu_video_render_mutex_unlock(); }
static void render_frame(double alpha, int perspective) { //printf("--- render frame ---\n"); //float t = g_snes_height / 512.0; //fs_log("%d %d %d %d\n", g_crop.x, g_crop.y, g_crop.w, g_crop.h); double s1; double s2; double t1; double t2; double emu_aspect; if (g_fs_emu_video_crop_mode) { //if (g_viewport_mode == FS_EMU_VIEWPORT_MODE_CROP) { s1 = (double) g_crop.x / g_frame_texture_width; s2 = (double) (g_crop.x + g_crop.w) / g_frame_texture_width; t1 = (double) g_crop.y / g_frame_texture_height; t2 = (double) (g_crop.y + g_crop.h) / g_frame_texture_height; emu_aspect = (double) g_crop.w / (double) g_crop.h; } else { s1 = 0.0; s2 = (double) g_frame_width / g_frame_texture_width; t1 = 0.0; t2 = (double) g_frame_height / g_frame_texture_height; emu_aspect = (double) g_frame_width / (double) g_frame_height; } emu_aspect *= g_frame_aspect; double x1 = -1.0; double x2 = 1.0; double y1 = -1.0; double y2 = 1.0; double repeat_right_border = 0; //int repeat_bottom_border = 0; if (fs_emu_video_get_aspect_correction()) { double screen_aspect = (double) fs_ml_video_width() / (double) fs_ml_video_height(); if (emu_aspect > screen_aspect) { // emu video is wider than screen double h = screen_aspect / emu_aspect; //double padding = (2.0 - 2.0 * h) / 2.0; double padding = 1.0 - h; if (g_effective_viewport_mode == FS_EMU_VIEWPORT_MODE_CROP) { y1 += padding; y2 -= padding; } else { // FS_EMU_VIEWPORT_MODE_CENTER t1 -= padding / 2.0; t2 += padding / 2.0; //y2 -= padding; } } else { double w = emu_aspect / screen_aspect; //double padding = (2.0 - 2.0 * w) / 2.0; double padding = 1.0 - w; if (g_effective_viewport_mode == FS_EMU_VIEWPORT_MODE_CROP) { x1 += padding; x2 -= padding; } else { // FS_EMU_VIEWPORT_MODE_CENTER //s1 -= padding / 4.0; // FIXME: THIS IS WRONG s1 -= padding / 2.0; //s2 += padding / 2.0; x2 -= padding; //repeat_right_border = x2; repeat_right_border = 1.0 - padding; } } } // if video is not stretched, we render black rectangles to cover // the rest of the screen if (x1 > -1.0 || x2 < 1.0 || y1 > -1.0 || y2 < 1.0) { fs_gl_texturing(0); if (alpha < 1.0) { fs_gl_blending(1); fs_gl_color4f(0.0, 0.0, 0.0, alpha); } else { fs_gl_blending(0); fs_gl_color4f(0.0, 0.0, 0.0, 1.0); } glEnableClientState(GL_VERTEX_ARRAY); if (x1 > -1.0) { GLfloat vert[] = { -1.0, y1, x1, y1, x1, y2, -1.0, y2 }; glVertexPointer(2, GL_FLOAT, 0, vert); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); CHECK_GL_ERROR(); } if (x2 < 1.0) { GLfloat vert[] = { x2, y1, 1.0, y1, 1.0, y2, x2, y2 }; glVertexPointer(2, GL_FLOAT, 0, vert); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); CHECK_GL_ERROR(); } if (y1 > -1.0) { GLfloat vert1[] = { x1, -1.0, x2, -1.0, x2, y1, x1, y1 }; GLfloat vert2[] = { -1.0, -1.0, -0.1, -1.0, -1.0, 0.0, -1.0, y1, 0.0, -1.0, y1, -0.1 }; glVertexPointer(2, GL_FLOAT, 0, vert1); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glVertexPointer(3, GL_FLOAT, 0, vert2); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); CHECK_GL_ERROR(); } if (y2 < 1.0) { GLfloat vert1[] = { x1, y2, x2, y2, x2, 1.0, x1, 1.0 }; GLfloat vert2[] = { -1.0, y2, -0.1, -1.0, y2, 0.0, -1.0, 1.0, 0.0, -1.0, 1.0, -0.1 }; glVertexPointer(2, GL_FLOAT, 0, vert1); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glVertexPointer(3, GL_FLOAT, 0, vert2); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); CHECK_GL_ERROR(); } glDisableClientState(GL_VERTEX_ARRAY); } //printf("--- render frame done ---\n"); if (perspective) { // render left side in 3d mode fs_gl_blending(0); if (x1 > -1.0) { // emu screen does not reach screen edge - side will be black fs_gl_texturing(0); fs_gl_color4f(0.0, 0.0, 0.0, alpha); } else { fs_gl_texturing(1); fs_gl_bind_texture(g_frame_texture); fs_gl_color4f(0.33 * alpha, 0.33 * alpha, 0.33 * alpha, alpha); } GLfloat tex[] = { s1, t2, s1, t2, s1, t1, // TODO: is s1 on this line a bug? s1, t1 }; GLfloat vert[] = { -1.0, y1, -0.1, -1.0, y1, 0.0, -1.0, y2, 0.0, -1.0, y2, -0.1 }; glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glVertexPointer(3, GL_FLOAT, 0, vert); glTexCoordPointer(2, GL_FLOAT, 0, tex); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); CHECK_GL_ERROR(); } float color = 1.0; if (g_frame_texture == 0) { // texture has not been created yet color = 0.0; fs_gl_texturing(0); } else { fs_gl_texturing(1); fs_gl_bind_texture(g_frame_texture); } if (alpha < 1.0) { fs_gl_blending(1); fs_gl_color4f(color * alpha, color * alpha, color * alpha, alpha); } else { fs_gl_blending(0); fs_gl_color4f(color, color, color, 1.0); } GLfloat tex[] = { s1, t2, s2, t2, s2, t1, s1, t1 }; GLfloat vert[] = { x1, y1, x2, y1, x2, y2, x1, y2 }; glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glVertexPointer(2, GL_FLOAT, 0, vert); glTexCoordPointer(2, GL_FLOAT, 0, tex); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); CHECK_GL_ERROR(); //repeat_right_border = 0; if (repeat_right_border > 0.0) { s1 = s2 = (double) (g_frame_width - 1) / g_frame_texture_width; GLfloat tex[] = { s1, t2, s2, t2, s2, t1, s1, t1 }; GLfloat vert[] = { repeat_right_border, y1, 1.0, y1, 1.0, y2, repeat_right_border, y2 }; glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glVertexPointer(2, GL_FLOAT, 0, vert); glTexCoordPointer(2, GL_FLOAT, 0, tex); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); CHECK_GL_ERROR(); // so the following code does not render black rectangle over // the right border x2 = 1.0; } }
static void render_pass(shader_pass *pass, int first, int last) { int output_w = -1; int output_h = -1; debug_printf("\n"); // If the horizontal scale method is fixed, set the output width to // the horizontal scale value. if (pass->hor_scale_method == SCALING_FIXED) { output_w = pass->hor_scale_value; } // If the horizontal scale method is input, set the output width to the // horizontal component of the current input size, multiplied by // the horizontal scale value. else if (pass->hor_scale_method == SCALING_INPUT) { output_w = g_cur_input_w * pass->hor_scale_value; } // If the horizontal scale method is output, set the output width to the // horizontal component of the final output size, multiplied by the // horizontal scale value. else if (pass->hor_scale_method == SCALING_OUTPUT) { output_w = g_final_output_w * pass->hor_scale_value; } // If this is the first shader pass, and the output width is not yet set, // the host application may set the output width to an arbitrary value. else if (first && !last) { output_w = 1024; debug_printf("setting output width to arbitrary value %d\n", output_w); } // If this is the last shader pass: if (last) { // If the output width is already set, set requires implicit // pass to True. if (output_w > 0) { g_requires_implicit_pass = 1; } // If the output width is not yet set, set the output width to // the horizontal component of the final output size. else { output_w = g_final_output_w; } } // Otherwise, set the output width to the horizontal component of // the current input size. if (output_w < 0) { output_w = g_cur_input_w; } // If the vertical scale method is fixed, set the output height to the // vertical scale value. if (pass->ver_scale_method == SCALING_FIXED) { output_h = pass->ver_scale_value; debug_printf("pass->ver_scale_method == SCALING_FIXED, " "output_h = %d\n", output_h); } // If the vertical scale method is input, set the output height to the // vertical component of the current input size, multiplied by the // vertical scale value. else if (pass->ver_scale_method == SCALING_INPUT) { output_h = g_cur_input_h * pass->ver_scale_value; debug_printf("pass->ver_scale_method == SCALING_INPUT, " "output_h = %d\n", output_h); } // If the vertical scale method is output, set the output height to the // vertical component of the final output size, multiplied by the vertical // scale value. else if (pass->ver_scale_method == SCALING_OUTPUT) { output_h = g_final_output_h * pass->ver_scale_value; debug_printf("pass->ver_scale_method == SCALING_OUTPUT, " "output_h = %d\n", output_h); } // If this is the first shader pass, and the output height is not yet // set, the host application may set the output height to an arbitrary // value. else if (first && !last) { output_h = 1024; debug_printf("setting output height to arbitrary value %d\n", output_h); } // If this is the last shader pass: if (last) { // If the output height is already set, set requires implicit pass // to True. if (output_h > 0) { g_requires_implicit_pass = 1; } // If the output height is not yet set, set the output height to // the vertical component of the final output size. else { output_h = g_final_output_h; } } // Otherwise, set the output height to the vertical component of the // current input size. if (output_h < 0) { output_h = g_cur_input_h; } // Calculate the output size by combining the output width and // the output height. // If this is not the last shader pass, or this is the last shader pass // and requires implicit pass is True: GLuint frame_buffer = 0; GLuint output_texture = 0; int output_texture_w = 0; int output_texture_h = 0; //g_requires_implicit_pass = 1; if (!last || g_requires_implicit_pass) { // Construct the output texture with a suitable set of dimensions // larger than the output size. if (!pass->texture) { glGenTextures(1, &pass->texture); CHECK_GL_ERROR(); } output_texture = pass->texture; output_texture_w = round_up_pow2(output_w); output_texture_h = round_up_pow2(output_h); // The following is a hack to fix compatibility with // CRT-interlaced-halation.shader which has a faulty assumption, // that rubyTextureSize and rubyOriginalTextureSize will be the // same in the final pass. The reason they may not be the same is // because of the crop / zoom function, so for example, FS-UAE may // allocate a 1024x1024 texture, but because of cropping, a // 1024x512 will be big enough for subsequent passes. if (output_texture_h < g_orig_texture_h) { output_texture_h = g_orig_texture_h; } if (output_texture_w < g_orig_texture_w) { output_texture_w = g_orig_texture_w; } fs_gl_bind_texture(output_texture); // using RGBA ensures that line size in bytes is a multiple of 4 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, output_texture_w, output_texture_h, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); CHECK_GL_ERROR(); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); fs_gl_bind_texture(0); // Construct a framebuffer object, and bind the output texture to it // as GL_COLOR_ATTACHMENT0. // Tell OpenGL to render to the frame-buffer object. if (!pass->frame_buffer) { //debug_printf("generating frame buffer\n"); glGenFramebuffers(1, &pass->frame_buffer); CHECK_GL_ERROR(); } frame_buffer = pass->frame_buffer; glPushMatrix(); glLoadIdentity(); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); debug_printf("%d %d\n", output_w, output_h); glViewport(0, 0, output_w, output_h); glBindFramebuffer(GL_FRAMEBUFFER, frame_buffer); CHECK_GL_ERROR(); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, output_texture, 0); CHECK_GL_ERROR(); if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { debug_printf("fbo is not complete!\n"); } glClear(GL_COLOR_BUFFER_BIT); } // Set the GL_TEXTURE_MAG_FILTER and GL_TEXTURE_MIN_FILTER parameters // of the current texture according to the filter setting in the current // shader pass. //glBindTexture(GL_TEXTURE_2D, g_cur_texture); fs_gl_bind_texture(g_cur_texture); CHECK_GL_ERROR(); if (pass->filtering == FILTERING_NEAREST) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); CHECK_GL_ERROR(); // Tell OpenGL to use the shader program handle from the current shader // pass as the active shader program. glUseProgram(pass->program); CHECK_GL_ERROR(); GLint rubyFrameCount = glGetUniformLocation(pass->program, "rubyFrameCount"); if (rubyFrameCount >= 0) { glUniform1i(rubyFrameCount, g_frame_count); } CHECK_GL_ERROR(); // Set the uniform rubyOrigTexture to the original texture. GLint rubyOrigTexture = glGetUniformLocation(pass->program, "rubyOrigTexture"); if (rubyOrigTexture >= 0) { debug_printf("set rubyOrigTexture to %d\n", 1); glUniform1i(rubyOrigTexture, 1); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, g_orig_texture); glActiveTexture(GL_TEXTURE0); } CHECK_GL_ERROR(); // Set the uniform rubyOrigTextureSize to the original texture size. GLint rubyOrigTextureSize = glGetUniformLocation(pass->program, "rubyOrigTextureSize"); if (rubyOrigTextureSize >= 0) { debug_printf("set rubyOrigTextureSize to %dx%d\n", g_orig_texture_w, g_orig_texture_h); glUniform2f(rubyOrigTextureSize, g_orig_texture_w, g_orig_texture_h); } CHECK_GL_ERROR(); // Set the uniform rubyOrigInputSize to the original input size. GLint rubyOrigInputSize = glGetUniformLocation(pass->program, "rubyOrigInputSize"); if (rubyOrigInputSize >= 0) { debug_printf("set rubyOrigInputSize to %dx%d\n", g_orig_input_w, g_orig_input_h); glUniform2f(rubyOrigInputSize, g_orig_input_w, g_orig_input_h); } CHECK_GL_ERROR(); // Set the uniform rubyTexture to the current texture. GLint rubyTexture = glGetUniformLocation(pass->program, "rubyTexture"); if (rubyTexture >= 0) { debug_printf("set rubyTexture to %d\n", 0); glUniform1i(rubyTexture, 0); } CHECK_GL_ERROR(); // Set the uniform rubyTextureSize to the current texture size. GLint rubyTextureSize = glGetUniformLocation(pass->program, "rubyTextureSize"); if (rubyTextureSize >= 0) { debug_printf("set rubyTextureSize to %dx%d\n", g_cur_texture_w, g_cur_texture_h); glUniform2f(rubyTextureSize, g_cur_texture_w, g_cur_texture_h); } CHECK_GL_ERROR(); // Set the uniform rubyInputSize to the current input size. GLint rubyInputSize = glGetUniformLocation(pass->program, "rubyInputSize"); if (rubyInputSize >= 0) { debug_printf("set rubyInputSize to %dx%d\n", g_cur_input_w, g_cur_input_h); glUniform2f(rubyInputSize, g_cur_input_w, g_cur_input_h); } CHECK_GL_ERROR(); // Set the uniform rubyOutputSize to the output size. GLint rubyOutputSize = glGetUniformLocation(pass->program, "rubyOutputSize"); if (rubyOutputSize >= 0) { debug_printf("set rubyOutputSize to %dx%d\n", output_w, output_h); glUniform2f(rubyOutputSize, output_w, output_h); } CHECK_GL_ERROR(); // Render a quad, textured with the current texture, with a vertex at // each corner of the output size. float s2 = (float) g_cur_input_w / (float) g_cur_texture_w; float t2 = (float) g_cur_input_h / (float) g_cur_texture_h; fs_gl_bind_texture(g_cur_texture); render_quad(s2, 0.0, t2, first, last && !g_requires_implicit_pass, first); CHECK_GL_ERROR(); // Tell OpenGL not to use a shader program (i.e. glUseProgram(0)). glUseProgram(0); CHECK_GL_ERROR(); // If this is not the last shader pass, or this is the last shader pass // and requires implicit pass is True: if (!last || g_requires_implicit_pass) { // Tell OpenGL not to use the frame-buffer object. debug_printf("unbind framebuffer\n"); glBindFramebuffer(GL_FRAMEBUFFER, 0); CHECK_GL_ERROR(); glViewport(0, 0, fs_ml_video_width(), fs_ml_video_height()); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); } // Set the current input size to the current output size. g_cur_input_w = output_w; g_cur_input_h = output_h; // Set the current texture size to the size of the output texture. g_cur_texture_w = output_texture_w; g_cur_texture_h = output_texture_h; // Set the current texture to the output texture. g_cur_texture = output_texture; }