Add FPS limiter. Added support for OpenGL screen rendering (not 3D emulation). Added command line options.

This commit is contained in:
masscat 2007-05-23 16:38:00 +00:00
parent 38edaf280f
commit e0a6babdae
1 changed files with 504 additions and 15 deletions

View File

@ -23,6 +23,18 @@
#include <string.h>
#include <libgen.h>
/*
* FIXME: Not sure how to detect OpenGL in a platform portable way.
*/
#ifdef HAVE_GL_GL_H
#define INCLUDE_OPENGL_2D 1
#endif
#ifdef INCLUDE_OPENGL_2D
#include <GL/gl.h>
#include <GL/glu.h>
#endif
#ifndef CLI_UI
#define CLI_UI
#endif
@ -37,7 +49,21 @@
volatile BOOL execute = FALSE;
SDL_Surface * surface;
static float nds_screen_size_ratio = 1.0f;
#define DISPLAY_FPS 1
#ifdef DISPLAY_FPS
#define NUM_FRAMES_TO_TIME 60
#endif
#define FPS_LIMITER_FRAME_PERIOD 8
static SDL_Surface * surface;
/* Flags to pass to SDL_SetVideoMode */
static int sdl_videoFlags = 0;
SoundInterface_struct *SNDCoreList[] = {
&SNDDummy,
@ -69,37 +95,377 @@ const u16 cli_kb_cfg[NB_KEYS] =
SDLK_o // BOOST
};
int Draw() {
struct my_config {
int disable_sound;
#ifdef INCLUDE_OPENGL_2D
int opengl_2d;
int soft_colour_convert;
#endif
const char *nds_file;
};
static void
init_config( struct my_config *config) {
config->disable_sound = 0;
config->nds_file = NULL;
#ifdef INCLUDE_OPENGL_2D
config->opengl_2d = 0;
config->soft_colour_convert = 0;
#endif
}
static int
fill_config( struct my_config *config,
int argc, char ** argv) {
int good_args = 1;
int print_usage = 0;
int i;
for ( i = 1; i < argc && good_args; i++) {
if ( strcmp( argv[i], "--help") == 0) {
printf( "USAGE: %s <nds-file>\n", argv[0]);
printf( "OPTIONS:\n");
printf( " --disable-sound Disables the sound emulation\n");
#ifdef INCLUDE_OPENGL_2D
printf( " --opengl-2d Enables using OpenGL for screen rendering\n");
printf( " --soft-convert Use software colour conversion during OpenGL\n");
printf( " screen rendering. May produce better or worse\n");
printf( " frame rates depending on hardware.\n");
#endif
printf( "\n");
printf( " --help Display this message\n");
good_args = 0;
}
else if ( strcmp( argv[i], "--disable-sound") == 0) {
config->disable_sound = 1;
}
#ifdef INCLUDE_OPENGL_2D
else if ( strcmp( argv[i], "--opengl-2d") == 0) {
config->opengl_2d = 1;
}
else if ( strcmp( argv[i], "--soft-convert") == 0) {
config->soft_colour_convert = 1;
}
#endif
else {
if ( config->nds_file == NULL) {
config->nds_file = argv[i];
}
else {
fprintf( stderr, "NDS file (\"%s\") already set\n", config->nds_file);
good_args = 0;
}
}
}
if ( good_args) {
if ( config->nds_file == NULL) {
print_usage = 1;
good_args = 0;
}
}
if ( print_usage) {
fprintf( stderr, "USAGE: %s <nds-file>\n", argv[0]);
}
return good_args;
}
/**
* A SDL timer callback function. Signals the supplied SDL semaphore
* if its value is small.
*
* @param interval The interval since the last call (in ms)
* @param param The pointer to the semaphore.
*
* @return The interval to the next call (required by SDL)
*/
static Uint32
fps_limiter_fn( Uint32 interval, void *param) {
SDL_sem *sdl_semaphore = (SDL_sem *)param;
/* signal the semaphore if it is getting low */
if ( SDL_SemValue( sdl_semaphore) < 4) {
SDL_SemPost( sdl_semaphore);
}
return interval;
}
#ifdef INCLUDE_OPENGL_2D
/* initialization openGL function */
static int
initGL( GLuint *screen_texture) {
GLenum errCode;
int init_good = 1;
int i;
u16 blank_texture[256 * 512];
for ( i = 0; i < 256 * 512; i++) {
blank_texture[i] = 0x001f;
}
/* Enable Texture Mapping */
glEnable( GL_TEXTURE_2D );
/* Set the background black */
glClearColor( 0.0f, 0.0f, 0.0f, 0.5f );
/* Depth buffer setup */
glClearDepth( 1.0f );
/* Enables Depth Testing */
glEnable( GL_DEPTH_TEST );
/* The Type Of Depth Test To Do */
glDepthFunc( GL_LEQUAL );
/* Create The Texture */
glGenTextures( 1, &screen_texture[0]);
glBindTexture( GL_TEXTURE_2D, screen_texture[0]);
/* Generate The Texture */
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, 256, 512,
0, GL_RGBA,
GL_UNSIGNED_SHORT_1_5_5_5_REV,
blank_texture);
/* Linear Filtering */
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
if ((errCode = glGetError()) != GL_NO_ERROR) {
const GLubyte *errString;
errString = gluErrorString(errCode);
fprintf( stderr, "Failed to init GL: %s\n", errString);
init_good = 0;
}
return init_good;
}
static void
resizeWindow( u16 width, u16 height) {
int comp_width = 3 * width;
int comp_height = 2 * height;
int use_width = 1;
GLenum errCode;
/* Height / width ration */
GLfloat ratio;
if ( comp_width > comp_height) {
use_width = 0;
}
/* Protect against a divide by zero */
if ( height == 0 )
height = 1;
if ( width == 0)
width = 1;
ratio = ( GLfloat )width / ( GLfloat )height;
/* Setup our viewport. */
glViewport( 0, 0, ( GLint )width, ( GLint )height );
/*
* change to the projection matrix and set
* our viewing volume.
*/
glMatrixMode( GL_PROJECTION );
glLoadIdentity( );
{
double left;
double right;
double bottom;
double top;
double other_dimen;
if ( use_width) {
left = 0.0;
right = 256.0;
nds_screen_size_ratio = 256.0 / (double)width;
other_dimen = (double)width * 3.0 / 2.0;
top = 0.0;
bottom = 384.0 * ((double)height / other_dimen);
}
else {
top = 0.0;
bottom = 384.0;
nds_screen_size_ratio = 384.0 / (double)height;
other_dimen = (double)height * 2.0 / 3.0;
left = 0.0;
right = 256.0 * ((double)width / other_dimen);
}
/*
printf("%d,%d\n", width, height);
printf("l %lf, r %lf, t %lf, b %lf, other dimen %lf\n",
left, right, top, bottom, other_dimen);
*/
/* get the area (0,0) to (256,384) into the middle of the viewport */
gluOrtho2D( left, right, bottom, top);
}
/* Make sure we're chaning the model view and not the projection */
glMatrixMode( GL_MODELVIEW );
/* Reset The View */
glLoadIdentity( );
if ((errCode = glGetError()) != GL_NO_ERROR) {
const GLubyte *errString;
errString = gluErrorString(errCode);
fprintf( stderr, "GL resize failed: %s\n", errString);
}
surface = SDL_SetVideoMode( width, height, 32,
sdl_videoFlags );
}
static void
opengl_Draw( GLuint *texture, int software_convert) {
GLenum errCode;
/* Clear The Screen And The Depth Buffer */
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
/* Move Into The Screen 5 Units */
glLoadIdentity( );
/* Select screen Texture */
glBindTexture( GL_TEXTURE_2D, texture[0]);
if ( software_convert) {
int i;
u8 converted[256 * 384 * 3];
for ( i = 0; i < (256 * 384); i++) {
converted[(i * 3) + 0] = ((*((u16 *)&GPU_screen[(i<<1)]) >> 0) & 0x1f) << 3;
converted[(i * 3) + 1] = ((*((u16 *)&GPU_screen[(i<<1)]) >> 5) & 0x1f) << 3;
converted[(i * 3) + 2] = ((*((u16 *)&GPU_screen[(i<<1)]) >> 10) & 0x1f) << 3;
}
glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 256, 384,
GL_RGB,
GL_UNSIGNED_BYTE,
converted);
}
else {
glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 256, 384,
GL_RGBA,
GL_UNSIGNED_SHORT_1_5_5_5_REV,
&GPU_screen);
}
if ((errCode = glGetError()) != GL_NO_ERROR) {
const GLubyte *errString;
errString = gluErrorString(errCode);
fprintf( stderr, "GL subimage failed: %s\n", errString);
}
/* Draw the screen as a textured quad */
glBegin( GL_QUADS);
glTexCoord2f( 0.0f, 0.0f ); glVertex3f( 0.0f, 0.0f, 0.0f );
glTexCoord2f( 1.0f, 0.0f ); glVertex3f( 256.0f, 0.0f, 0.0f );
glTexCoord2f( 1.0f, 0.75f ); glVertex3f( 256.0f, 384.0f, 0.0f );
glTexCoord2f( 0.0f, 0.75f ); glVertex3f( 0.0f, 384.0f, 0.0f );
glEnd( );
if ((errCode = glGetError()) != GL_NO_ERROR) {
const GLubyte *errString;
errString = gluErrorString(errCode);
fprintf( stderr, "GL draw failed: %s\n", errString);
}
/* Draw it to the screen */
SDL_GL_SwapBuffers( );
}
#endif
static void
Draw( void) {
SDL_Surface *rawImage;
rawImage = SDL_CreateRGBSurfaceFrom((void*)&GPU_screen, 256, 384, 16, 512, 0x001F, 0x03E0, 0x7C00, 0);
if(rawImage == NULL) return 1;
if(rawImage == NULL) return;
SDL_BlitSurface(rawImage, 0, surface, 0);
SDL_UpdateRect(surface, 0, 0, 0, 0);
SDL_FreeSurface(rawImage);
return 1;
return;
}
int main(int argc, char ** argv) {
static unsigned short keypad = 0;
struct my_config my_config;
u32 last_cycle = 0;
int limiter_frame_counter = 0;
SDL_sem *fps_limiter_semaphore;
SDL_TimerID limiter_timer;
int sdl_quit = 0;
#ifdef DISPLAY_FPS
u32 fps_timing = 0;
u32 fps_frame_counter = 0;
u32 fps_previous_time = 0;
u32 fps_temp_time;
#endif
#ifdef INCLUDE_OPENGL_2D
GLuint screen_texture[1];
#endif
/* this holds some info about our display */
const SDL_VideoInfo *videoInfo;
init_config( &my_config);
if ( !fill_config( &my_config, argc, argv)) {
exit(1);
}
#ifdef DEBUG
LogStart();
#endif
NDS_Init();
SPU_ChangeSoundCore(SNDCORE_SDL, 735 * 4);
if (argc < 2) {
fprintf(stderr, "usage: %s filename\n", argv[0]);
return 1;
if ( !my_config.disable_sound) {
SPU_ChangeSoundCore(SNDCORE_SDL, 735 * 4);
}
if (NDS_LoadROM(argv[1], MC_TYPE_AUTODETECT, 1) < 0) {
fprintf(stderr, "error while loading %s\n", argv[1]);
return 2;
if ( NDS_LoadROM( my_config.nds_file, MC_TYPE_AUTODETECT, 1) < 0) {
fprintf(stderr, "error while loading %s\n", my_config.nds_file);
exit(-1);
}
/* // This has to get fixed yet
@ -110,7 +476,7 @@ int main(int argc, char ** argv) {
execute = TRUE;
if(SDL_Init(SDL_INIT_VIDEO) == -1)
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) == -1)
{
fprintf(stderr, "Error trying to initialize SDL: %s\n",
SDL_GetError());
@ -118,16 +484,98 @@ int main(int argc, char ** argv) {
}
SDL_WM_SetCaption("Desmume SDL", NULL);
/* Fetch the video info */
videoInfo = SDL_GetVideoInfo( );
if ( !videoInfo ) {
fprintf( stderr, "Video query failed: %s\n", SDL_GetError( ) );
exit( -1);
}
/* This checks if hardware blits can be done */
if ( videoInfo->blit_hw )
sdl_videoFlags |= SDL_HWACCEL;
#ifdef INCLUDE_OPENGL_2D
if ( my_config.opengl_2d) {
/* the flags to pass to SDL_SetVideoMode */
sdl_videoFlags = SDL_OPENGL; /* Enable OpenGL in SDL */
sdl_videoFlags |= SDL_GL_DOUBLEBUFFER; /* Enable double buffering */
sdl_videoFlags |= SDL_HWPALETTE; /* Store the palette in hardware */
sdl_videoFlags |= SDL_RESIZABLE; /* Enable window resizing */
/* This checks to see if surfaces can be stored in memory */
if ( videoInfo->hw_available )
sdl_videoFlags |= SDL_HWSURFACE;
else
sdl_videoFlags |= SDL_SWSURFACE;
/* Sets up OpenGL double buffering */
SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
surface = SDL_SetVideoMode( 256, 192 * 2, 32,
sdl_videoFlags );
/* Verify there is a surface */
if ( !surface ) {
fprintf( stderr, "Video mode set failed: %s\n", SDL_GetError( ) );
exit( -1);
}
/* initialize OpenGL */
if ( !initGL( screen_texture)) {
fprintf( stderr, "Failed to init GL, fall back to software render\n");
my_config.opengl_2d = 0;
}
}
if ( !my_config.opengl_2d) {
#endif
sdl_videoFlags |= SDL_SWSURFACE;
surface = SDL_SetVideoMode(256, 384, 32, sdl_videoFlags);
if ( !surface ) {
fprintf( stderr, "Video mode set failed: %s\n", SDL_GetError( ) );
exit( -1);
}
#ifdef INCLUDE_OPENGL_2D
}
/* set the initial window size */
if ( my_config.opengl_2d) {
resizeWindow( 256, 192*2);
}
#endif
/* Initialize joysticks */
if(!init_joy()) return 1;
/* Load our own keyboard configuration */
set_kb_keys(cli_kb_cfg);
surface = SDL_SetVideoMode(256, 384, 32, SDL_SWSURFACE);
/* create the semaphore used for fps limiting */
fps_limiter_semaphore = SDL_CreateSemaphore( 1);
/* start a SDL timer for every FPS_LIMITER_FRAME_PERIOD frames to keep us at 60 fps */
limiter_timer = SDL_AddTimer( 16 * FPS_LIMITER_FRAME_PERIOD,
fps_limiter_fn, fps_limiter_semaphore);
if ( limiter_timer == NULL) {
fprintf( stderr, "Error trying to start FPS limiter timer: %s\n",
SDL_GetError());
return 1;
}
while(!sdl_quit) {
/* Look for queued events and update keypad status */
keypad = process_ctrls_events(keypad);
#ifdef INCLUDE_OPENGL_2D
sdl_quit = process_ctrls_events( &keypad, resizeWindow, nds_screen_size_ratio);
#else
sdl_quit = process_ctrls_events( &keypad, NULL, nds_screen_size_ratio);
#endif
/* Update mouse position and click */
if(mouse.down) NDS_setTouchPos(mouse.x, mouse.y);
if(mouse.click)
@ -139,12 +587,53 @@ int main(int argc, char ** argv) {
update_keypad(keypad); /* Update keypad */
last_cycle = NDS_exec((560190 << 1) - last_cycle, FALSE);
SPU_Emulate();
#ifdef INCLUDE_OPENGL_2D
if ( my_config.opengl_2d) {
opengl_Draw( screen_texture, my_config.soft_colour_convert);
}
else
#endif
Draw();
limiter_frame_counter += 1;
if ( limiter_frame_counter >= FPS_LIMITER_FRAME_PERIOD) {
limiter_frame_counter = 0;
/* wait for the timer to expire */
SDL_SemWait( fps_limiter_semaphore);
}
#ifdef DISPLAY_FPS
fps_frame_counter += 1;
fps_temp_time = SDL_GetTicks();
fps_timing += fps_temp_time - fps_previous_time;
fps_previous_time = fps_temp_time;
if ( fps_frame_counter == NUM_FRAMES_TO_TIME) {
char win_title[100];
float fps = (float)fps_timing;
fps /= NUM_FRAMES_TO_TIME * 1000.f;
fps = 1.0f / fps;
//printf("fps %f\n", fps);
fps_frame_counter = 0;
fps_timing = 0;
sprintf( win_title, "%f Desmume", fps);
SDL_WM_SetCaption( win_title, NULL);
}
#endif
}
/* Unload joystick */
uninit_joy();
/* tidy up the FPS limiter timer and semaphore */
SDL_RemoveTimer( limiter_timer);
SDL_DestroySemaphore( fps_limiter_semaphore);
SDL_Quit();
NDS_DeInit();
#ifdef DEBUG