RetroArch/gfx/gl.c

454 lines
13 KiB
C
Raw Normal View History

2010-05-28 16:22:06 +00:00
/* SSNES - A Super Ninteno Entertainment System (SNES) Emulator frontend for libsnes.
* Copyright (C) 2010 - Hans-Kristian Arntzen
*
* Some code herein may be based on code found in BSNES.
*
* SSNES 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 Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SSNES 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 SSNES.
* If not, see <http://www.gnu.org/licenses/>.
*/
2010-11-08 22:38:32 +00:00
#define GL_GLEXT_PROTOTYPES
2010-05-28 16:22:06 +00:00
2010-05-28 00:45:18 +00:00
#include "driver.h"
2010-11-12 22:09:43 +00:00
#include "config.h"
2010-05-28 00:45:18 +00:00
#include <GL/glfw.h>
#include <GL/glext.h>
2010-05-28 00:45:18 +00:00
#include <stdint.h>
#include "libsnes.hpp"
#include <stdio.h>
2010-08-16 17:49:54 +00:00
#include <sys/time.h>
2010-12-26 03:15:12 +00:00
#include <string.h>
2010-05-28 00:45:18 +00:00
2010-11-12 22:09:43 +00:00
#ifdef HAVE_CG
2010-11-12 21:32:38 +00:00
#include <Cg/cg.h>
#include <Cg/cgGL.h>
2010-11-12 22:09:43 +00:00
#endif
2010-11-12 21:32:38 +00:00
static const GLfloat vertexes[] = {
0, 0, 0,
0, 1, 0,
1, 1, 0,
1, 0, 0
};
2010-11-12 21:32:38 +00:00
static const GLfloat tex_coords[] = {
0, 1,
2010-11-08 23:13:57 +00:00
0, 0,
2010-11-12 21:32:38 +00:00
1, 0,
1, 1
2010-11-08 23:13:57 +00:00
};
2010-11-13 01:48:32 +00:00
static bool keep_aspect = true;
2010-11-13 02:31:37 +00:00
#ifdef HAVE_CG
2010-11-13 02:13:10 +00:00
static CGparameter cg_mvp_matrix;
2010-11-13 02:31:37 +00:00
#endif
static GLuint gl_width = 0, gl_height = 0;
2010-05-28 00:45:18 +00:00
typedef struct gl
{
2010-08-16 16:40:17 +00:00
bool vsync;
2010-11-13 01:48:32 +00:00
#ifdef HAVE_CG
CGcontext cgCtx;
2010-11-13 02:13:10 +00:00
CGprogram cgFPrg;
CGprogram cgVPrg;
CGprofile cgFProf;
2010-11-13 01:48:32 +00:00
CGprofile cgVProf;
CGparameter cg_video_size, cg_texture_size, cg_output_size;
CGparameter cg_Vvideo_size, cg_Vtexture_size, cg_Voutput_size; // Vertexes
2010-11-13 01:48:32 +00:00
#endif
GLuint texture;
GLuint tex_filter;
2010-12-26 03:15:12 +00:00
unsigned last_width;
unsigned last_height;
unsigned tex_w, tex_h;
GLfloat tex_coords[8];
2010-05-28 00:45:18 +00:00
} gl_t;
static void glfw_input_poll(void *data)
{
(void)data;
glfwPollEvents();
}
2010-10-01 18:38:27 +00:00
#define BUTTONS_MAX 128
#define AXES_MAX 128
2010-10-01 18:38:27 +00:00
static unsigned joypad_id[2];
static unsigned joypad_buttons[2];
static unsigned joypad_axes[2];
2010-10-01 18:15:45 +00:00
static bool joypad_inited = false;
static unsigned joypad_count = 0;
2010-05-28 00:45:18 +00:00
2010-10-01 18:15:45 +00:00
static int init_joypads(int max_pads)
{
// Finds the first (two) joypads that are alive
int count = 0;
2010-10-01 18:38:27 +00:00
for ( int i = GLFW_JOYSTICK_1; (i <= GLFW_JOYSTICK_LAST) && (count < max_pads); i++ )
2010-05-28 00:45:18 +00:00
{
if ( glfwGetJoystickParam(i, GLFW_PRESENT) == GL_TRUE )
{
2010-10-01 18:15:45 +00:00
joypad_id[count] = i;
joypad_buttons[count] = glfwGetJoystickParam(i, GLFW_BUTTONS);
2010-10-01 18:38:27 +00:00
if (joypad_buttons[count] > BUTTONS_MAX)
joypad_buttons[count] = BUTTONS_MAX;
joypad_axes[count] = glfwGetJoystickParam(i, GLFW_AXES);
if (joypad_axes[count] > AXES_MAX)
joypad_axes[count] = AXES_MAX;
2010-10-01 18:15:45 +00:00
count++;
2010-05-28 00:45:18 +00:00
}
}
2010-10-01 18:15:45 +00:00
joypad_inited = true;
return count;
}
2010-05-28 00:45:18 +00:00
static bool glfw_is_pressed(int port_num, const struct snes_keybind *key, unsigned char *buttons, float *axes)
{
if (glfwGetKey(key->key))
return true;
if (port_num >= joypad_count)
return false;
if (key->joykey < joypad_buttons[port_num] && buttons[key->joykey] == GLFW_PRESS)
return true;
if (key->joyaxis != AXIS_NONE)
{
if (AXIS_NEG_GET(key->joyaxis) < joypad_axes[port_num] && axes[AXIS_NEG_GET(key->joyaxis)] <= -AXIS_THRESHOLD)
return true;
if (AXIS_POS_GET(key->joyaxis) < joypad_axes[port_num] && axes[AXIS_POS_GET(key->joyaxis)] >= AXIS_THRESHOLD)
return true;
}
return false;
}
2010-10-01 18:15:45 +00:00
static int16_t glfw_input_state(void *data, const struct snes_keybind **binds, bool port, unsigned device, unsigned index, unsigned id)
{
if ( device != SNES_DEVICE_JOYPAD )
return 0;
if ( !joypad_inited )
joypad_count = init_joypads(2);
int port_num = port ? 1 : 0;
2010-10-01 18:38:27 +00:00
unsigned char buttons[BUTTONS_MAX];
float axes[AXES_MAX];
2010-10-01 18:15:45 +00:00
if ( joypad_count > port_num )
{
2010-10-01 18:15:45 +00:00
glfwGetJoystickButtons(joypad_id[port_num], buttons, joypad_buttons[port_num]);
glfwGetJoystickPos(joypad_id[port_num], axes, joypad_axes[port_num]);
}
2010-10-01 18:15:45 +00:00
const struct snes_keybind *snes_keybinds;
if (port == SNES_PORT_1)
snes_keybinds = binds[0];
else
snes_keybinds = binds[1];
2010-05-28 00:45:18 +00:00
// Checks if button is pressed, and sets fast-forwarding state
bool pressed = false;
2010-10-01 18:15:45 +00:00
for ( int i = 0; snes_keybinds[i].id != -1; i++ )
2010-08-16 16:40:17 +00:00
if ( snes_keybinds[i].id == SNES_FAST_FORWARD_KEY )
set_fast_forward_button(glfw_is_pressed(port_num, &snes_keybinds[i], buttons, axes));
else if ( !pressed && snes_keybinds[i].id == (int)id )
pressed = glfw_is_pressed(port_num, &snes_keybinds[i], buttons, axes);
2010-05-28 00:45:18 +00:00
return pressed;
2010-05-28 00:45:18 +00:00
}
static void glfw_free_input(void *data)
{
free(data);
}
2010-06-27 12:57:37 +00:00
static const input_driver_t input_glfw = {
2010-05-28 00:45:18 +00:00
.poll = glfw_input_poll,
.input_state = glfw_input_state,
.free = glfw_free_input
};
static void GLFWCALL resize(int width, int height)
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
GLuint out_width = width, out_height = height;
2010-05-28 00:45:18 +00:00
2010-05-28 00:54:20 +00:00
if ( keep_aspect )
2010-05-28 00:45:18 +00:00
{
2010-11-08 22:38:32 +00:00
float desired_aspect = 4.0/3;
float device_aspect = (float)width / height;
2010-05-28 00:45:18 +00:00
2010-11-08 22:38:32 +00:00
// If the aspect ratios of screen and desired aspect ratio are sufficiently equal (floating point stuff),
// assume they are actually equal.
if ( (int)(device_aspect*1000) > (int)(desired_aspect*1000) )
2010-05-28 00:45:18 +00:00
{
2010-11-08 22:38:32 +00:00
float delta = (desired_aspect / device_aspect - 1.0) / 2.0 + 0.5;
glViewport(width * (0.5 - delta), 0, 2.0 * width * delta, height);
out_width = (int)(2.0 * width * delta);
2010-05-28 00:45:18 +00:00
}
2010-11-08 22:38:32 +00:00
else if ( (int)(device_aspect*1000) < (int)(desired_aspect*1000) )
2010-05-28 00:45:18 +00:00
{
2010-11-08 22:38:32 +00:00
float delta = (device_aspect / desired_aspect - 1.0) / 2.0 + 0.5;
glViewport(0, height * (0.5 - delta), width, 2.0 * height * delta);
out_height = (int)(2.0 * height * delta);
2010-05-28 00:45:18 +00:00
}
else
2010-11-08 22:38:32 +00:00
glViewport(0, 0, width, height);
2010-05-28 00:45:18 +00:00
}
else
2010-11-08 22:38:32 +00:00
glViewport(0, 0, width, height);
2010-05-28 00:45:18 +00:00
2010-11-08 22:38:32 +00:00
glOrtho(0, 1, 0, 1, -1, 1);
2010-05-28 00:45:18 +00:00
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
2010-11-13 02:31:37 +00:00
#ifdef HAVE_CG
2010-11-13 02:13:10 +00:00
cgGLSetStateMatrixParameter(cg_mvp_matrix, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY);
2010-11-13 02:31:37 +00:00
#endif
gl_width = out_width;
gl_height = out_height;
2010-05-28 00:45:18 +00:00
}
2010-08-16 17:49:54 +00:00
static float tv_to_fps(const struct timeval *tv, const struct timeval *new_tv, int frames)
{
float time = new_tv->tv_sec - tv->tv_sec + (new_tv->tv_usec - tv->tv_usec)/1000000.0;
return frames/time;
}
static inline void show_fps(void)
2010-05-28 00:45:18 +00:00
{
2010-12-14 13:28:09 +00:00
// Shows FPS in taskbar.
2010-08-16 17:49:54 +00:00
static int frames = 0;
static struct timeval tv;
struct timeval new_tv;
2010-12-14 13:28:09 +00:00
2010-08-16 17:49:54 +00:00
if (frames == 0)
gettimeofday(&tv, NULL);
if ((frames % 180) == 0 && frames > 0)
2010-08-16 17:49:54 +00:00
{
gettimeofday(&new_tv, NULL);
2010-12-14 14:19:13 +00:00
struct timeval tmp_tv = tv;
2010-08-16 17:49:54 +00:00
gettimeofday(&tv, NULL);
char tmpstr[256] = {0};
float fps = tv_to_fps(&tmp_tv, &new_tv, 180);
2010-08-16 17:49:54 +00:00
snprintf(tmpstr, sizeof(tmpstr) - 1, "SSNES || FPS: %6.1f || Frames: %d", fps, frames);
glfwSetWindowTitle(tmpstr);
}
frames++;
2010-11-08 22:38:32 +00:00
}
2010-08-16 17:49:54 +00:00
2010-11-08 22:38:32 +00:00
static bool gl_frame(void *data, const uint16_t* frame, int width, int height, int pitch)
{
2010-11-13 01:48:32 +00:00
gl_t *gl = data;
2010-11-08 22:38:32 +00:00
glClear(GL_COLOR_BUFFER_BIT);
#if HAVE_CG
2010-11-13 01:48:32 +00:00
cgGLSetParameter2f(gl->cg_video_size, width, height);
2010-12-26 03:15:12 +00:00
cgGLSetParameter2f(gl->cg_texture_size, gl->tex_w, gl->tex_h);
cgGLSetParameter2f(gl->cg_output_size, gl_width, gl_height);
cgGLSetParameter2f(gl->cg_Vvideo_size, width, height);
2010-12-26 04:00:01 +00:00
cgGLSetParameter2f(gl->cg_Vtexture_size, gl->tex_w, gl->tex_h);
cgGLSetParameter2f(gl->cg_Voutput_size, gl_width, gl_height);
#endif
2010-12-26 03:15:12 +00:00
if (width != gl->last_width || height != gl->last_height) // res change. need to clear out texture.
{
gl->last_width = width;
gl->last_height = height;
glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
uint8_t *tmp = calloc(1, gl->tex_w * gl->tex_h * sizeof(uint16_t));
glTexSubImage2D(GL_TEXTURE_2D,
0, 0, 0, gl->tex_w, gl->tex_h, GL_BGRA,
GL_UNSIGNED_SHORT_1_5_5_5_REV, tmp);
free(tmp);
gl->tex_coords[0] = 0;
gl->tex_coords[1] = (GLfloat)height / gl->tex_h;
gl->tex_coords[2] = 0;
gl->tex_coords[3] = 0;
gl->tex_coords[4] = (GLfloat)width / gl->tex_w;
gl->tex_coords[5] = 0;
gl->tex_coords[6] = (GLfloat)width / gl->tex_w;
gl->tex_coords[7] = (GLfloat)height / gl->tex_h;
}
2010-11-12 21:32:38 +00:00
glPixelStorei(GL_UNPACK_ROW_LENGTH, pitch >> 1);
2010-12-26 03:15:12 +00:00
glTexSubImage2D(GL_TEXTURE_2D,
0, 0, 0, width, height, GL_BGRA,
2010-11-12 21:32:38 +00:00
GL_UNSIGNED_SHORT_1_5_5_5_REV, frame);
2010-11-08 22:38:32 +00:00
glDrawArrays(GL_QUADS, 0, 4);
2010-12-14 13:28:09 +00:00
2010-11-08 22:38:32 +00:00
show_fps();
2010-05-28 00:45:18 +00:00
glfwSwapBuffers();
return true;
}
static void gl_free(void *data)
{
2010-11-13 01:48:32 +00:00
gl_t *gl = data;
2010-11-12 22:09:43 +00:00
#ifdef HAVE_CG
2010-11-13 01:48:32 +00:00
cgDestroyContext(gl->cgCtx);
2010-11-12 22:09:43 +00:00
#endif
2010-11-08 22:38:32 +00:00
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
2010-11-13 01:48:32 +00:00
glDeleteTextures(1, &gl->texture);
2010-05-28 00:45:18 +00:00
glfwTerminate();
}
2010-08-16 16:40:17 +00:00
static void gl_set_nonblock_state(void *data, bool state)
{
gl_t *gl = data;
if (gl->vsync)
{
if (state)
glfwSwapInterval(0);
else
glfwSwapInterval(1);
}
}
2010-06-27 12:57:37 +00:00
static void* gl_init(video_info_t *video, const input_driver_t **input)
2010-05-28 00:45:18 +00:00
{
2010-08-16 16:40:17 +00:00
gl_t *gl = malloc(sizeof(gl_t));
if ( gl == NULL )
2010-05-28 00:45:18 +00:00
return NULL;
2010-05-29 12:45:40 +00:00
keep_aspect = video->force_aspect;
if ( video->smooth )
2010-11-13 01:48:32 +00:00
gl->tex_filter = GL_LINEAR;
2010-05-29 12:45:40 +00:00
else
2010-11-13 01:48:32 +00:00
gl->tex_filter = GL_NEAREST;
2010-05-28 00:54:20 +00:00
2010-05-28 00:45:18 +00:00
glfwInit();
int res;
2010-05-29 12:45:40 +00:00
res = glfwOpenWindow(video->width, video->height, 0, 0, 0, 0, 0, 0, (video->fullscreen) ? GLFW_FULLSCREEN : GLFW_WINDOW);
2010-05-28 00:45:18 +00:00
2010-11-08 22:38:32 +00:00
if (!res)
2010-05-28 00:45:18 +00:00
{
glfwTerminate();
return NULL;
}
glfwSetWindowSizeCallback(resize);
2010-05-29 12:45:40 +00:00
if ( video->vsync )
2010-05-28 00:45:18 +00:00
glfwSwapInterval(1); // Force vsync
else
glfwSwapInterval(0);
2010-08-16 16:40:17 +00:00
gl->vsync = video->vsync;
2010-05-28 00:45:18 +00:00
glEnable(GL_TEXTURE_2D);
2010-11-08 23:13:57 +00:00
glDisable(GL_DITHER);
2010-11-08 22:38:32 +00:00
glDisable(GL_DEPTH_TEST);
glColor3f(1, 1, 1);
glClearColor(0, 0, 0, 0);
2010-05-28 00:45:18 +00:00
glfwSetWindowTitle("SSNES");
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
2010-11-13 01:48:32 +00:00
glGenTextures(1, &gl->texture);
2010-11-08 22:38:32 +00:00
2010-11-13 01:48:32 +00:00
glBindTexture(GL_TEXTURE_2D, gl->texture);
2010-11-08 22:38:32 +00:00
2010-12-07 23:13:57 +00:00
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
2010-11-13 01:48:32 +00:00
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl->tex_filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl->tex_filter);
2010-11-08 22:38:32 +00:00
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(3, GL_FLOAT, 3 * sizeof(GLfloat), vertexes);
2010-12-26 03:15:12 +00:00
memcpy(gl->tex_coords, tex_coords, sizeof(tex_coords));
glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), gl->tex_coords);
gl->tex_w = 256 * video->input_scale;
gl->tex_h = 256 * video->input_scale;
uint8_t *tmp = calloc(1, gl->tex_w * gl->tex_h * sizeof(uint16_t));
glTexImage2D(GL_TEXTURE_2D,
0, GL_RGBA, gl->tex_w, gl->tex_h, 0, GL_BGRA,
GL_UNSIGNED_SHORT_1_5_5_5_REV, tmp);
free(tmp);
gl->last_width = gl->tex_w;
gl->last_height = gl->tex_h;
2010-05-28 00:45:18 +00:00
2010-11-12 22:09:43 +00:00
#ifdef HAVE_CG
2010-11-13 01:48:32 +00:00
gl->cgCtx = cgCreateContext();
if (gl->cgCtx == NULL)
2010-11-12 21:32:38 +00:00
{
fprintf(stderr, "Failed to create Cg context\n");
2010-11-12 22:09:43 +00:00
goto error;
2010-11-12 21:32:38 +00:00
}
2010-11-13 02:13:10 +00:00
gl->cgFProf = cgGLGetLatestProfile(CG_GL_FRAGMENT);
gl->cgVProf = cgGLGetLatestProfile(CG_GL_VERTEX);
if (gl->cgFProf == CG_PROFILE_UNKNOWN || gl->cgVProf == CG_PROFILE_UNKNOWN)
2010-11-12 21:32:38 +00:00
{
fprintf(stderr, "Invalid profile type\n");
2010-11-12 22:09:43 +00:00
goto error;
2010-11-12 21:32:38 +00:00
}
2010-11-13 02:13:10 +00:00
cgGLSetOptimalOptions(gl->cgFProf);
2010-11-13 01:48:32 +00:00
cgGLSetOptimalOptions(gl->cgVProf);
2010-11-13 02:13:10 +00:00
gl->cgFPrg = cgCreateProgramFromFile(gl->cgCtx, CG_SOURCE, cg_shader_path, gl->cgFProf, "main_fragment", 0);
gl->cgVPrg = cgCreateProgramFromFile(gl->cgCtx, CG_SOURCE, cg_shader_path, gl->cgVProf, "main_vertex", 0);
if (gl->cgFPrg == NULL || gl->cgVPrg == NULL)
2010-11-12 21:32:38 +00:00
{
CGerror err = cgGetError();
fprintf(stderr, "CG error: %s\n", cgGetErrorString(err));
2010-11-12 22:09:43 +00:00
goto error;
2010-11-12 21:32:38 +00:00
}
2010-11-13 02:13:10 +00:00
cgGLLoadProgram(gl->cgFPrg);
cgGLLoadProgram(gl->cgVPrg);
cgGLEnableProfile(gl->cgFProf);
2010-11-13 01:48:32 +00:00
cgGLEnableProfile(gl->cgVProf);
2010-11-13 02:13:10 +00:00
cgGLBindProgram(gl->cgFPrg);
cgGLBindProgram(gl->cgVPrg);
2010-11-13 02:13:10 +00:00
gl->cg_video_size = cgGetNamedParameter(gl->cgFPrg, "IN.video_size");
gl->cg_texture_size = cgGetNamedParameter(gl->cgFPrg, "IN.texture_size");
gl->cg_output_size = cgGetNamedParameter(gl->cgFPrg, "IN.output_size");
gl->cg_Vvideo_size = cgGetNamedParameter(gl->cgVPrg, "IN.video_size");
gl->cg_Vtexture_size = cgGetNamedParameter(gl->cgVPrg, "IN.texture_size");
gl->cg_Voutput_size = cgGetNamedParameter(gl->cgVPrg, "IN.output_size");
2010-11-13 02:13:10 +00:00
cg_mvp_matrix = cgGetNamedParameter(gl->cgVPrg, "modelViewProj");
cgGLSetStateMatrixParameter(cg_mvp_matrix, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY);
2010-11-12 22:09:43 +00:00
#endif
2010-11-12 21:32:38 +00:00
2010-06-27 12:40:06 +00:00
*input = &input_glfw;
2010-08-16 16:40:17 +00:00
return gl;
2010-11-13 02:31:37 +00:00
#ifdef HAVE_CG
2010-11-12 22:09:43 +00:00
error:
free(gl);
return NULL;
2010-11-13 02:31:37 +00:00
#endif
2010-05-28 00:45:18 +00:00
}
const video_driver_t video_gl = {
.init = gl_init,
.frame = gl_frame,
2010-08-16 16:40:17 +00:00
.set_nonblock_state = gl_set_nonblock_state,
2010-05-28 00:45:18 +00:00
.free = gl_free
};
2010-12-14 13:28:09 +00:00
2010-05-28 00:45:18 +00:00