Exemple #1
0
/**
 * 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();
}
Exemple #2
0
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
}
Exemple #3
0
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();
}
Exemple #4
0
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;
    }
}
Exemple #5
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;
}