From 1631e940f6ba62635b290fe5da1b116bd093cb4c Mon Sep 17 00:00:00 2001 From: Themaister Date: Wed, 29 Jun 2011 03:45:17 +0200 Subject: [PATCH 01/26] Fix up VSync calls for OSX. --- gfx/gl.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/gfx/gl.c b/gfx/gl.c index 40566b52f8..e142be8b24 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -987,6 +987,10 @@ static void gl_free(void *data) free(gl); } +#ifdef __APPLE__ +#include +#endif + static void gl_set_nonblock_state(void *data, bool state) { gl_t *gl = data; @@ -1001,13 +1005,8 @@ static void gl_set_nonblock_state(void *data, bool state) } if (wgl_swap_interval) wgl_swap_interval(state ? 0 : 1); #elif defined(__APPLE__) - // Will this work? - //AGLContext ctx = aglGetCurrentContext(); - //if (!ctx) - // return; - //GLint interval = state ? 0 : 1; - //aglSetInteger(ctx, AGL_SWAP_INTERVAL, &interval); - SSNES_WARN("This feature is currently not implemented for OSX.\n"); + GLint val = state ? 0 : 1; + CGLSetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &val); #else static int (*glx_swap_interval)(int) = NULL; if (!glx_swap_interval) From 36c6c211940fd9fe7ffe614409747c67e514d273 Mon Sep 17 00:00:00 2001 From: Themaister Date: Wed, 29 Jun 2011 04:54:30 +0200 Subject: [PATCH 02/26] Use RSOUND_CFLAGS, etc. --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 2d9c53294b..0cadf95c73 100644 --- a/Makefile +++ b/Makefile @@ -40,7 +40,8 @@ endif ifeq ($(HAVE_RSOUND), 1) OBJ += audio/rsound.o - LIBS += -lrsound + LIBS += $(RSOUND_LIBS) + DEFINES += $(RSOUND_CFLAGS) endif ifeq ($(HAVE_OSS), 1) From d81b9ee0a7b938fa8a884e7fa8ee96c64d6a53d9 Mon Sep 17 00:00:00 2001 From: Themaister Date: Sun, 3 Jul 2011 13:41:24 +0200 Subject: [PATCH 03/26] Print full compile error in Cg. --- gfx/shader_cg.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/gfx/shader_cg.c b/gfx/shader_cg.c index 973af2b03c..41f803ce8b 100644 --- a/gfx/shader_cg.c +++ b/gfx/shader_cg.c @@ -295,8 +295,11 @@ static bool load_plain(const char *path) { if (!prg[i].fprg || !prg[i].vprg) { + const char *listing = cgGetLastListing(cgCtx); CGerror err = cgGetError(); SSNES_ERR("CG error: %s\n", cgGetErrorString(err)); + if (listing) + SSNES_ERR("%s\n", listing); return false; } @@ -802,8 +805,11 @@ static bool load_preset(const char *path) if (!prog->fprg || !prog->vprg) { + const char *listing = cgGetLastListing(cgCtx); CGerror err = cgGetError(); SSNES_ERR("CG error: %s\n", cgGetErrorString(err)); + if (listing) + SSNES_ERR("%s\n", listing); goto error; } From 508cfdbda762e2d8572d633a43a80fcea25adf55 Mon Sep 17 00:00:00 2001 From: Themaister Date: Sun, 3 Jul 2011 15:39:35 +0200 Subject: [PATCH 04/26] Start implementing access to previous texture. --- gfx/gl.c | 104 +++++++++++++++++++++++++++++----------------- gfx/gl_common.h | 6 +-- gfx/shader_cg.c | 29 ++++++++++++- gfx/shader_cg.h | 1 + gfx/shader_glsl.c | 40 +++++++++++++++--- gfx/shader_glsl.h | 1 + 6 files changed, 133 insertions(+), 48 deletions(-) diff --git a/gfx/gl.c b/gfx/gl.c index e142be8b24..2866d4baad 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -125,7 +125,9 @@ static inline bool load_gl_proc(void) { return true; } typedef struct gl { bool vsync; - GLuint texture; + GLuint texture[2]; + unsigned tex_index; // For use with PREV. + struct gl_tex_info prev_info; GLuint tex_filter; void *empty_buf; @@ -153,8 +155,8 @@ typedef struct gl unsigned win_height; unsigned vp_width, vp_out_width; unsigned vp_height, vp_out_height; - unsigned last_width; - unsigned last_height; + unsigned last_width[2]; + unsigned last_height[2]; unsigned tex_w, tex_h; GLfloat tex_coords[8]; #ifdef HAVE_FBO @@ -255,20 +257,21 @@ static void gl_shader_set_params(unsigned width, unsigned height, unsigned out_width, unsigned out_height, unsigned frame_count, const struct gl_tex_info *info, + const struct gl_tex_info *prev_info, const struct gl_tex_info *fbo_info, unsigned fbo_info_cnt) { #ifdef HAVE_CG gl_cg_set_params(width, height, tex_width, tex_height, out_width, out_height, - frame_count, info, fbo_info, fbo_info_cnt); + frame_count, info, prev_info, fbo_info, fbo_info_cnt); #endif #ifdef HAVE_XML gl_glsl_set_params(width, height, tex_width, tex_height, out_width, out_height, - frame_count, info, fbo_info, fbo_info_cnt); + frame_count, info, prev_info, fbo_info, fbo_info_cnt); #endif } @@ -338,7 +341,7 @@ static inline void gl_init_font(gl_t *gl, const char *font_path, unsigned font_s glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glBindTexture(GL_TEXTURE_2D, gl->texture); + glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]); } else SSNES_WARN("Couldn't init font renderer with font \"%s\"...\n", font_path); @@ -653,7 +656,7 @@ static void gl_render_msg(gl_t *gl, const char *msg) // Go back to old rendering path. glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), gl->tex_coords); glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), vertexes_flipped); - glBindTexture(GL_TEXTURE_2D, gl->texture); + glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]); glDisable(GL_BLEND); #endif } @@ -712,6 +715,10 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei gl_shader_use(1); gl->frame_count++; +#if defined(HAVE_XML) || defined(HAVE_CG) + glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]); +#endif + #ifdef HAVE_FBO // Render to texture in first pass. if (gl->fbo_inited) @@ -767,7 +774,7 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei last_max_height = gl->fbo_rect[i].max_img_height; } - glBindTexture(GL_TEXTURE_2D, gl->texture); + glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]); pglBindFramebuffer(GL_FRAMEBUFFER, gl->fbo[0]); gl->render_to_tex = true; set_viewport(gl, gl->fbo_rect[0].img_width, gl->fbo_rect[0].img_height, true); @@ -814,7 +821,7 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei } // Go back to what we're supposed to do, render to FBO #0 :D - glBindTexture(GL_TEXTURE_2D, gl->texture); + glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]); pglBindFramebuffer(GL_FRAMEBUFFER, gl->fbo[0]); set_viewport(gl, gl->fbo_rect[0].img_width, gl->fbo_rect[0].img_height, true); } @@ -823,10 +830,10 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei #endif } - if ((width != gl->last_width || height != gl->last_height) && gl->empty_buf) // Res change. need to clear out texture. + if ((width != gl->last_width[gl->tex_index] || height != gl->last_height[gl->tex_index]) && gl->empty_buf) // Res change. need to clear out texture. { - gl->last_width = width; - gl->last_height = height; + gl->last_width[gl->tex_index] = width; + gl->last_height[gl->tex_index] = height; glPixelStorei(GL_UNPACK_ALIGNMENT, get_alignment(pitch)); glPixelStorei(GL_UNPACK_ROW_LENGTH, gl->tex_w); @@ -839,13 +846,13 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei set_texture_coords(gl->tex_coords, xamt, yamt); } - - // Work around a certain issue a Cg where not using TEXUNIT0 - // in shader causes cgGLEnableTextureParameter() causes it - // to bind to TEXUNIT0, to avoid really funny bugs, rebind - // our texture. -#ifdef HAVE_CG - glBindTexture(GL_TEXTURE_2D, gl->texture); +#if defined(HAVE_XML) || defined(HAVE_CG) + else if (width != gl->last_width[1 - gl->tex_index] || height != gl->last_height[1 - gl->tex_index]) + { + GLfloat xamt = (GLfloat)width / gl->tex_w; + GLfloat yamt = (GLfloat)height / gl->tex_h; + set_texture_coords(gl->tex_coords, xamt, yamt); + } #endif #ifdef HAVE_FBO @@ -861,7 +868,7 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei gl->texture_fmt, frame); struct gl_tex_info tex_info = { - .tex = gl->texture, + .tex = gl->texture[gl->tex_index], .input_size = {width, height}, .tex_size = {gl->tex_w, gl->tex_h} }; @@ -871,7 +878,7 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei glClear(GL_COLOR_BUFFER_BIT); gl_shader_set_params(width, height, gl->tex_w, gl->tex_h, gl->vp_width, gl->vp_height, gl->frame_count, - &tex_info, fbo_tex_info, fbo_tex_info_cnt); + &tex_info, &gl->prev_info, fbo_tex_info, fbo_tex_info_cnt); glDrawArrays(GL_QUADS, 0, 4); @@ -916,7 +923,7 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei gl_shader_set_params(prev_rect->img_width, prev_rect->img_height, prev_rect->width, prev_rect->height, gl->vp_width, gl->vp_height, gl->frame_count, - &tex_info, fbo_tex_info, fbo_tex_info_cnt); + &tex_info, &gl->prev_info, fbo_tex_info, fbo_tex_info_cnt); glDrawArrays(GL_QUADS, 0, 4); @@ -942,7 +949,7 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei gl_shader_set_params(prev_rect->img_width, prev_rect->img_height, prev_rect->width, prev_rect->height, gl->vp_width, gl->vp_height, gl->frame_count, - &tex_info, fbo_tex_info, fbo_tex_info_cnt); + &tex_info, &gl->prev_info, fbo_tex_info, fbo_tex_info_cnt); glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), vertexes_flipped); glDrawArrays(GL_QUADS, 0, 4); @@ -951,6 +958,11 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei } #endif +#if defined(HAVE_XML) || defined(HAVE_CG) + memcpy(&gl->prev_info, &tex_info, sizeof(tex_info)); + gl->tex_index = 1 - gl->tex_index; +#endif + if (msg) gl_render_msg(gl, msg); @@ -970,7 +982,7 @@ static void gl_free(void *data) gl_shader_deinit(); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDeleteTextures(1, &gl->texture); + glDeleteTextures(2, gl->texture); #ifdef HAVE_FBO if (gl->fbo_inited) @@ -1133,15 +1145,18 @@ static void* gl_init(const video_info_t *video, const input_driver_t **input, vo glMatrixMode(GL_MODELVIEW); glLoadIdentity(); - glGenTextures(1, &gl->texture); + glGenTextures(2, gl->texture); - glBindTexture(GL_TEXTURE_2D, gl->texture); + for (unsigned i = 0; i < 2; i++) + { + glBindTexture(GL_TEXTURE_2D, gl->texture[i]); - 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_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl->tex_filter); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl->tex_filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl->tex_filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl->tex_filter); + } glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); @@ -1155,15 +1170,30 @@ static void* gl_init(const video_info_t *video, const input_driver_t **input, vo gl->tex_w = 256 * video->input_scale; gl->tex_h = 256 * video->input_scale; - glTexImage2D(GL_TEXTURE_2D, - 0, GL_RGBA, gl->tex_w, gl->tex_h, 0, gl->texture_type, - gl->texture_fmt, NULL); - // Empty buffer that we use to clear out the texture with on res change. gl->empty_buf = calloc(gl->base_size, gl->tex_w * gl->tex_h); - gl->last_width = gl->tex_w; - gl->last_height = gl->tex_h; + for (unsigned i = 0; i < 2; i++) + { + glBindTexture(GL_TEXTURE_2D, gl->texture[i]); + glTexImage2D(GL_TEXTURE_2D, + 0, GL_RGBA, gl->tex_w, gl->tex_h, 0, gl->texture_type, + gl->texture_fmt, gl->empty_buf ? gl->empty_buf : NULL); + } + glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]); + + for (unsigned i = 0; i < 2; i++) + { + gl->last_width[i] = gl->tex_w; + gl->last_height[i] = gl->tex_h; + } + + gl->prev_info.tex = gl->texture[1 - gl->tex_index]; + gl->prev_info.input_size[0] = gl->tex_w; + gl->prev_info.tex_size[0] = gl->tex_w; + gl->prev_info.input_size[1] = gl->tex_h; + gl->prev_info.tex_size[1] = gl->tex_h; + memcpy(gl->prev_info.coord, tex_coords, sizeof(tex_coords)); // Hook up SDL input driver to get SDL_QUIT events and RESIZE. sdl_input_t *sdl_input = input_sdl.init(); @@ -1222,7 +1252,7 @@ static bool gl_xml_shader(void *data, const char *path) gl->render_to_tex = false; gl->fbo_pass = 0; - glBindTexture(GL_TEXTURE_2D, gl->texture); + glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]); } #endif diff --git a/gfx/gl_common.h b/gfx/gl_common.h index 2c6d14750b..300bb13786 100644 --- a/gfx/gl_common.h +++ b/gfx/gl_common.h @@ -101,9 +101,9 @@ struct gl_fbo_scale struct gl_tex_info { GLuint tex; - float input_size[2]; - float tex_size[2]; - float coord[8]; + GLfloat input_size[2]; + GLfloat tex_size[2]; + GLfloat coord[8]; }; // Not legal to cast void* to fn-pointer. Need dirty hack to be compilant. diff --git a/gfx/shader_cg.c b/gfx/shader_cg.c index 41f803ce8b..2cfe2130ce 100644 --- a/gfx/shader_cg.c +++ b/gfx/shader_cg.c @@ -112,6 +112,7 @@ struct cg_program struct cg_fbo_params fbo[MAX_SHADERS]; struct cg_fbo_params orig; + struct cg_fbo_params prev; }; #define FILTER_UNSPEC 0 @@ -148,6 +149,7 @@ void gl_cg_set_params(unsigned width, unsigned height, unsigned out_width, unsigned out_height, unsigned frame_count, const struct gl_tex_info *info, + const struct gl_tex_info *prev_info, const struct gl_tex_info *fbo_info, unsigned fbo_info_cnt) { @@ -169,7 +171,6 @@ void gl_cg_set_params(unsigned width, unsigned height, if (param) { cgGLSetTextureParameter(param, info->tex); - //fprintf(stderr, "ORIGtex = (%d) %d\n", cgGLGetTextureParameter(param), cgGLGetTextureEnum(param) - GL_TEXTURE0); cgGLEnableTextureParameter(param); } @@ -183,6 +184,24 @@ void gl_cg_set_params(unsigned width, unsigned height, cgGLEnableClientState(prg[active_index].orig.coord); } + // Set prev texture + param = prg[active_index].prev.tex; + if (param) + { + cgGLSetTextureParameter(param, prev_info->tex); + cgGLEnableTextureParameter(param); + } + + set_param_2f(prg[active_index].prev.vid_size_v, prev_info->input_size[0], prev_info->input_size[1]); + set_param_2f(prg[active_index].prev.vid_size_f, prev_info->input_size[0], prev_info->input_size[1]); + set_param_2f(prg[active_index].prev.tex_size_v, prev_info->tex_size[0], prev_info->tex_size[1]); + set_param_2f(prg[active_index].prev.tex_size_f, prev_info->tex_size[0], prev_info->tex_size[1]); + if (prg[active_index].prev.coord) + { + cgGLSetParameterPointer(prg[active_index].prev.coord, 2, GL_FLOAT, 0, prev_info->coord); + cgGLEnableClientState(prg[active_index].prev.coord); + } + // Set lookup textures. for (unsigned i = 0; i < lut_textures_num; i++) { @@ -191,7 +210,6 @@ void gl_cg_set_params(unsigned width, unsigned height, { cgGLSetTextureParameter(param, lut_textures[i]); cgGLEnableTextureParameter(param); - //fprintf(stderr, "LUTtex = (%d) %d\n", cgGLGetTextureParameter(param), cgGLGetTextureEnum(param) - GL_TEXTURE0); } } @@ -908,6 +926,13 @@ bool gl_cg_init(const char *path) prg[i].orig.tex_size_f = cgGetNamedParameter(prg[i].fprg, "ORIG.texture_size"); prg[i].orig.coord = cgGetNamedParameter(prg[i].vprg, "ORIG.tex_coord"); + prg[i].prev.tex = cgGetNamedParameter(prg[i].fprg, "PREV.texture"); + prg[i].prev.vid_size_v = cgGetNamedParameter(prg[i].vprg, "PREV.video_size"); + prg[i].prev.vid_size_f = cgGetNamedParameter(prg[i].fprg, "PREV.video_size"); + prg[i].prev.tex_size_v = cgGetNamedParameter(prg[i].vprg, "PREV.texture_size"); + prg[i].prev.tex_size_f = cgGetNamedParameter(prg[i].fprg, "PREV.texture_size"); + prg[i].prev.coord = cgGetNamedParameter(prg[i].vprg, "PREV.tex_coord"); + for (unsigned j = 0; j < i - 1; j++) { char attr_buf[64]; diff --git a/gfx/shader_cg.h b/gfx/shader_cg.h index 306f65f17b..f0d8147c80 100644 --- a/gfx/shader_cg.h +++ b/gfx/shader_cg.h @@ -33,6 +33,7 @@ void gl_cg_set_params(unsigned width, unsigned height, unsigned out_width, unsigned out_height, unsigned frame_count, const struct gl_tex_info *info, + const struct gl_tex_info *prev_info, const struct gl_tex_info *fbo_info, unsigned fbo_info_cnt); void gl_cg_use(unsigned index); diff --git a/gfx/shader_glsl.c b/gfx/shader_glsl.c index caf360129d..0a5196ceee 100644 --- a/gfx/shader_glsl.c +++ b/gfx/shader_glsl.c @@ -1005,8 +1005,16 @@ void gl_glsl_set_params(unsigned width, unsigned height, unsigned out_width, unsigned out_height, unsigned frame_count, const struct gl_tex_info *info, + const struct gl_tex_info *prev_info, const struct gl_tex_info *fbo_info, unsigned fbo_info_cnt) { + // We enforce a certain layout for our various texture types in the texunits. + // Unit 0: Regular SNES frame (rubyTexture). + // Unit 1-A: LUT textures. + // Unit A+1: Previous texture. + // Unit A+2: Original texture. + // Unit A+3-B: FBO textures. + if (glsl_enable && gl_program[active_index] > 0) { GLint location; @@ -1032,15 +1040,34 @@ void gl_glsl_set_params(unsigned width, unsigned height, pglUniform1i(location, i + 1); } + // Set previous texture. + pglActiveTexture(GL_TEXTURE0 + gl_teximage_cnt + 1); + glBindTexture(GL_TEXTURE_2D, prev_info->tex); + location = pglGetUniformLocation(gl_program[active_index], "rubyPrevTexture"); + pglUniform1i(location, gl_teximage_cnt + 1); + + location = pglGetUniformLocation(gl_program[active_index], "rubyPrevTextureSize"); + pglUniform2fv(location, 1, prev_info->tex_size); + location = pglGetUniformLocation(gl_program[active_index], "rubyPrevInputSize"); + pglUniform2fv(location, 1, prev_info->input_size); + + // Pass texture coordinates. + location = pglGetAttribLocation(gl_program[active_index], "rubyPrevTexCoord"); + if (location >= 0) + { + pglEnableVertexAttribArray(location); + pglVertexAttribPointer(location, 2, GL_FLOAT, GL_FALSE, 0, prev_info->coord); + } + // Set original texture unless we're in first pass (pointless). if (active_index > 1) { // Bind original texture. - pglActiveTexture(GL_TEXTURE0 + gl_teximage_cnt + 1); + pglActiveTexture(GL_TEXTURE0 + gl_teximage_cnt + 2); glBindTexture(GL_TEXTURE_2D, info->tex); location = pglGetUniformLocation(gl_program[active_index], "rubyOrigTexture"); - pglUniform1i(location, gl_teximage_cnt + 1); + pglUniform1i(location, gl_teximage_cnt + 2); location = pglGetUniformLocation(gl_program[active_index], "rubyOrigTextureSize"); pglUniform2fv(location, 1, info->tex_size); @@ -1055,7 +1082,7 @@ void gl_glsl_set_params(unsigned width, unsigned height, pglVertexAttribPointer(location, 2, GL_FLOAT, GL_FALSE, 0, info->coord); } - GLuint base_tex = GL_TEXTURE0 + gl_teximage_cnt + 2; + GLuint base_tex = GL_TEXTURE0 + gl_teximage_cnt + 3; // Bind new texture in the chain. if (fbo_info_cnt > 0) @@ -1093,14 +1120,15 @@ void gl_glsl_set_params(unsigned width, unsigned height, else { // First pass, so unbind everything to avoid collitions. - pglActiveTexture(GL_TEXTURE0 + gl_teximage_cnt + 1); + // Unbind ORIG. + pglActiveTexture(GL_TEXTURE0 + gl_teximage_cnt + 2); glBindTexture(GL_TEXTURE_2D, 0); - GLuint base_tex = GL_TEXTURE0 + gl_teximage_cnt + 2; + GLuint base_tex = GL_TEXTURE0 + gl_teximage_cnt + 3; // Unbind any lurking FBO passes. // Rendering to a texture that is bound to a texture unit // sounds very shaky ... ;) - for (int i = 0; i < gl_num_programs; i++) + for (unsigned i = 0; i < gl_num_programs; i++) { pglActiveTexture(GL_TEXTURE0 + base_tex + i); glBindTexture(GL_TEXTURE_2D, 0); diff --git a/gfx/shader_glsl.h b/gfx/shader_glsl.h index d0d85ead8b..ad77c57100 100644 --- a/gfx/shader_glsl.h +++ b/gfx/shader_glsl.h @@ -33,6 +33,7 @@ void gl_glsl_set_params(unsigned width, unsigned height, unsigned out_width, unsigned out_height, unsigned frame_counter, const struct gl_tex_info *info, + const struct gl_tex_info *prev_info, const struct gl_tex_info *fbo_info, unsigned fbo_info_cnt); void gl_glsl_use(unsigned index); From 19a46b1f8b12fca921c1cefbd3d216a6e23a4d00 Mon Sep 17 00:00:00 2001 From: Themaister Date: Sun, 3 Jul 2011 15:53:56 +0200 Subject: [PATCH 05/26] Seems to work in Cg as well. --- gfx/shader_cg.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/gfx/shader_cg.c b/gfx/shader_cg.c index 2cfe2130ce..4ea6b4624b 100644 --- a/gfx/shader_cg.c +++ b/gfx/shader_cg.c @@ -36,18 +36,15 @@ static const char* stock_cg_program = "void main_vertex" "(" " float4 position : POSITION," - " float4 color : COLOR," " float2 texCoord : TEXCOORD0," "" " uniform float4x4 modelViewProj," "" " out float4 oPosition : POSITION," - " out float4 oColor : COLOR," " out float2 otexCoord : TEXCOORD0" ")" "{" " oPosition = mul(modelViewProj, position);" - " oColor = color;" " otexCoord = texCoord;" "}" "" From 783ab7acd58122a0ab4ab00452ad5c3df8968ce7 Mon Sep 17 00:00:00 2001 From: Themaister Date: Mon, 4 Jul 2011 13:33:09 +0200 Subject: [PATCH 06/26] Improve error handling. --- gfx/shader_cg.c | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/gfx/shader_cg.c b/gfx/shader_cg.c index 4ea6b4624b..b44d714168 100644 --- a/gfx/shader_cg.c +++ b/gfx/shader_cg.c @@ -288,16 +288,31 @@ static bool load_plain(const char *path) if (strlen(g_settings.video.second_pass_shader) > 0) SSNES_LOG("Loading 2nd pass: %s\n", g_settings.video.second_pass_shader); + char *listing[3] = {NULL}; + const char *list = NULL; + prg[0].fprg = cgCreateProgram(cgCtx, CG_SOURCE, stock_cg_program, cgFProf, "main_fragment", 0); prg[0].vprg = cgCreateProgram(cgCtx, CG_SOURCE, stock_cg_program, cgVProf, "main_vertex", 0); + list = cgGetLastListing(cgCtx); + if (list) + listing[0] = strdup(list); + prg[1].fprg = cgCreateProgramFromFile(cgCtx, CG_SOURCE, path, cgFProf, "main_fragment", 0); prg[1].vprg = cgCreateProgramFromFile(cgCtx, CG_SOURCE, path, cgVProf, "main_vertex", 0); + list = cgGetLastListing(cgCtx); + if (list) + listing[1] = strdup(list); + if (strlen(g_settings.video.second_pass_shader) > 0) { prg[2].fprg = cgCreateProgramFromFile(cgCtx, CG_SOURCE, g_settings.video.second_pass_shader, cgFProf, "main_fragment", 0); prg[2].vprg = cgCreateProgramFromFile(cgCtx, CG_SOURCE, g_settings.video.second_pass_shader, cgVProf, "main_vertex", 0); + + list = cgGetLastListing(cgCtx); + if (list) + listing[2] = strdup(list); cg_shader_num = 2; } else @@ -306,16 +321,15 @@ static bool load_plain(const char *path) cg_shader_num = 1; } - for (int i = 0; i < cg_shader_num + 1; i++) + for (unsigned i = 0; i <= cg_shader_num; i++) { if (!prg[i].fprg || !prg[i].vprg) { - const char *listing = cgGetLastListing(cgCtx); CGerror err = cgGetError(); SSNES_ERR("CG error: %s\n", cgGetErrorString(err)); - if (listing) - SSNES_ERR("%s\n", listing); - return false; + if (listing[i]) + SSNES_ERR("%s\n", listing[i]); + goto error; } cgGLLoadProgram(prg[i].fprg); @@ -323,6 +337,14 @@ static bool load_plain(const char *path) } return true; + +error: + for (unsigned i = 0; i < 3; i++) + { + if (listing[i]) + free(listing[i]); + } + return false; } #define print_buf(buf, ...) snprintf(buf, sizeof(buf), __VA_ARGS__) @@ -899,7 +921,7 @@ bool gl_cg_init(const char *path) if (prg[0].mvp) cgGLSetStateMatrixParameter(prg[0].mvp, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY); - for (unsigned i = 1; i < cg_shader_num + 1; i++) + for (unsigned i = 1; i <= cg_shader_num; i++) { cgGLBindProgram(prg[i].fprg); cgGLBindProgram(prg[i].vprg); From ab601ee3dc8ae78b5e3613e9a54aa6bc4db2e111 Mon Sep 17 00:00:00 2001 From: Themaister Date: Wed, 6 Jul 2011 16:54:54 +0200 Subject: [PATCH 07/26] v0.6.2 --- Makefile.win32 | 2 +- Makefile.win64 | 2 +- qb/config.params.sh | 2 +- ssnes.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile.win32 b/Makefile.win32 index f3921336d4..9524b66fea 100644 --- a/Makefile.win32 +++ b/Makefile.win32 @@ -155,7 +155,7 @@ clean: rm -f tools/*.o dist: all - zip -r ssnes-win32-0.6.1.zip $(TARGET) ssnes.cfg snes.dll libxml2.dll iconv.dll zlib1.dll SDL.dll freetype6.dll xaudio-c.dll rsound.dll pthreadGC2.dll cg.dll cgGL.dll libjpeg-8.dll libpng15-15.dll python32.dll SDL_image.dll $(JTARGET) + zip -r ssnes-win32-0.6.2.zip $(TARGET) ssnes.cfg snes.dll libxml2.dll iconv.dll zlib1.dll SDL.dll freetype6.dll xaudio-c.dll rsound.dll pthreadGC2.dll cg.dll cgGL.dll libjpeg-8.dll libpng15-15.dll python32.dll SDL_image.dll $(JTARGET) libs: wget https://github.com/downloads/Themaister/SSNES/SSNES-win32-libs.zip --no-check-certificate diff --git a/Makefile.win64 b/Makefile.win64 index f49265fc47..cfaa2eafea 100644 --- a/Makefile.win64 +++ b/Makefile.win64 @@ -142,7 +142,7 @@ clean: rm -f tools/*.o dist: all - zip -r ssnes-win64-0.6.1.zip $(TARGET) ssnes.cfg snes.dll xaudio-c.dll README.win32.txt $(JTARGET) + zip -r ssnes-win64-0.6.2.zip $(TARGET) ssnes.cfg snes.dll xaudio-c.dll README.win32.txt $(JTARGET) libs: wget https://github.com/downloads/Themaister/SSNES/ssnes-win64-libs.zip --no-check-certificate diff --git a/qb/config.params.sh b/qb/config.params.sh index c836906a92..6f526649d4 100644 --- a/qb/config.params.sh +++ b/qb/config.params.sh @@ -1,7 +1,7 @@ . qb/qb.params.sh PACKAGE_NAME=ssnes -PACKAGE_VERSION=0.6.1 +PACKAGE_VERSION=0.6.2 # Adds a command line opt to ./configure --help # $1: Variable (HAVE_ALSA, HAVE_OSS, etc) diff --git a/ssnes.c b/ssnes.c index 95ce235284..216bc035a1 100644 --- a/ssnes.c +++ b/ssnes.c @@ -340,7 +340,7 @@ static void fill_pathname_noext(char *out_path, const char *in_path, const char #endif #ifdef _WIN32 -#define PACKAGE_VERSION "0.6.1" +#define PACKAGE_VERSION "0.6.2" #endif #include "config.features.h" From 033860ceb97e104da94cb5a90aba4201388bf58f Mon Sep 17 00:00:00 2001 From: Themaister Date: Sat, 9 Jul 2011 08:37:08 +0200 Subject: [PATCH 08/26] Add -f/--fullscreen command line option. --- docs/ssnes.1 | 4 ++++ general.h | 1 + settings.c | 9 +++++++-- ssnes.c | 8 +++++++- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/docs/ssnes.1 b/docs/ssnes.1 index 0177d0e024..3ec6a5a980 100644 --- a/docs/ssnes.1 +++ b/docs/ssnes.1 @@ -40,6 +40,10 @@ Without this flag, the save state path will be inferred from the rom path name, When rom is loaded from \fBstdin\fR, this flag is mandatory to define as no path can be inferred. Do note that save states are bound to the libsnes implementation being used. Using a different libsnes could invalidate the save state file. +.TP +\fB--fullscreen, -f\fR +Always starts SSNES in fullscreen. Disregards settings in configuration file. + .TP \fB--config PATH, -c PATH\fR Sets the configuration file path. \fBssnes\fR will use this path to load the configuration file. diff --git a/general.h b/general.h index abc84a4936..923659edc2 100644 --- a/general.h +++ b/general.h @@ -147,6 +147,7 @@ struct global bool verbose; bool audio_active; bool video_active; + bool force_fullscreen; bool has_mouse[2]; bool has_scope[2]; diff --git a/settings.c b/settings.c index 3c5411cadf..d8ebeadb7c 100644 --- a/settings.c +++ b/settings.c @@ -111,7 +111,7 @@ static void set_defaults(void) g_settings.video.xscale = xscale; g_settings.video.yscale = yscale; - g_settings.video.fullscreen = fullscreen; + g_settings.video.fullscreen = g_extern.force_fullscreen ? true : fullscreen; g_settings.video.fullscreen_x = fullscreen_x; g_settings.video.fullscreen_y = fullscreen_y; g_settings.video.force_16bit = force_16bit; @@ -289,7 +289,12 @@ static void parse_config_file(void) CONFIG_GET_DOUBLE(video.yscale, "video_yscale"); CONFIG_GET_INT(video.fullscreen_x, "video_fullscreen_x"); CONFIG_GET_INT(video.fullscreen_y, "video_fullscreen_y"); - CONFIG_GET_BOOL(video.fullscreen, "video_fullscreen"); + + if (!g_extern.force_fullscreen) + { + CONFIG_GET_BOOL(video.fullscreen, "video_fullscreen"); + } + CONFIG_GET_BOOL(video.force_16bit, "video_force_16bit"); CONFIG_GET_BOOL(video.vsync, "video_vsync"); CONFIG_GET_BOOL(video.smooth, "video_smooth"); diff --git a/ssnes.c b/ssnes.c index 216bc035a1..6e9cc556dd 100644 --- a/ssnes.c +++ b/ssnes.c @@ -383,6 +383,7 @@ static void print_help(void) puts("Usage: ssnes [rom file] [options...]"); puts("\t-h/--help: Show this help message."); puts("\t-s/--save: Path for save file (*.srm). Required when rom is input from stdin."); + puts("\t-f/--fullscreen: Start SSNES in fullscreen regardless of config settings."); puts("\t-S/--savestate: Path to use for save states. If not selected, *.state will be assumed."); #ifdef HAVE_CONFIGFILE puts("\t-c/--config: Path for config file." SSNES_DEFAULT_CONF_PATH_STR); @@ -441,6 +442,7 @@ static void parse_input(int argc, char *argv[]) struct option opts[] = { { "help", 0, NULL, 'h' }, { "save", 1, NULL, 's' }, + { "fullscreen", 0, NULL, 'f' }, #ifdef HAVE_FFMPEG { "record", 1, NULL, 'r' }, #endif @@ -485,7 +487,7 @@ static void parse_input(int argc, char *argv[]) #define CONFIG_FILE_ARG #endif - char optstring[] = "hs:vS:m:p4jJg:b:B:Y:Z:P:HC:F:U:DN:X:" FFMPEG_RECORD_ARG CONFIG_FILE_ARG; + char optstring[] = "hs:fvS:m:p4jJg:b:B:Y:Z:P:HC:F:U:DN:X:" FFMPEG_RECORD_ARG CONFIG_FILE_ARG; for(;;) { val = 0; @@ -518,6 +520,10 @@ static void parse_input(int argc, char *argv[]) g_extern.has_set_save_path = true; break; + case 'f': + g_extern.force_fullscreen = true; + break; + case 'g': strlcpy(g_extern.gb_rom_path, optarg, sizeof(g_extern.gb_rom_path)); g_extern.game_type = SSNES_CART_SGB; From f3cdb95b04757e7e1197343df0a305cfb9dedc91 Mon Sep 17 00:00:00 2001 From: Themaister Date: Tue, 26 Jul 2011 00:53:24 +0200 Subject: [PATCH 09/26] Fix endian issues in OSS driver. --- audio/oss.c | 4 +++- audio/pulse.c | 12 ------------ general.h | 12 ++++++++++++ movie.c | 12 ------------ 4 files changed, 15 insertions(+), 25 deletions(-) diff --git a/audio/oss.c b/audio/oss.c index 272de6405e..d34a91351a 100644 --- a/audio/oss.c +++ b/audio/oss.c @@ -20,6 +20,7 @@ #endif #include "driver.h" +#include "general.h" #include #ifdef HAVE_OSS_BSD @@ -68,7 +69,8 @@ static void* __oss_init(const char* device, unsigned rate, unsigned latency) } int channels = 2; - int format = AFMT_S16_LE; + int format = is_little_endian() ? + AFMT_S16_LE : AFMT_S16_BE; if (ioctl(*fd, SNDCTL_DSP_CHANNELS, &channels) < 0) { diff --git a/audio/pulse.c b/audio/pulse.c index 526927bdd7..e1fdfb17ce 100644 --- a/audio/pulse.c +++ b/audio/pulse.c @@ -59,18 +59,6 @@ static void __pulse_free(void *data) } } -static inline uint8_t is_little_endian(void) -{ - union - { - uint16_t x; - uint8_t y[2]; - } u; - - u.x = 1; - return u.y[0]; -} - static void context_state_cb(pa_context *c, void *data) { pa_t *pa = data; diff --git a/general.h b/general.h index 923659edc2..9494a8ed92 100644 --- a/general.h +++ b/general.h @@ -305,6 +305,18 @@ static inline uint32_t next_pow2(uint32_t v) return v; } +static inline uint8_t is_little_endian(void) +{ + union + { + uint16_t x; + uint8_t y[2]; + } u; + + u.x = 1; + return u.y[0]; +} + #endif diff --git a/movie.c b/movie.c index 8a43ff3bff..b587704a34 100644 --- a/movie.c +++ b/movie.c @@ -107,18 +107,6 @@ struct bsv_movie #define CRC_INDEX 2 #define STATE_SIZE_INDEX 3 -static inline uint8_t is_little_endian(void) -{ - union - { - uint16_t u16; - uint8_t u8[2]; - } u; - - u.u16 = 1; - return u.u8[0]; -} - // Convert to big-endian if needed static inline uint32_t swap_if_big32(uint32_t val) { From 776291a13790e111b07f0f4e638dc225c827f578 Mon Sep 17 00:00:00 2001 From: Themaister Date: Thu, 4 Aug 2011 18:45:40 +0200 Subject: [PATCH 10/26] Start working on DirectSound driver. Untested so far. --- Makefile.win32 | 7 + Makefile.win64 | 7 + audio/dsound.c | 351 +++++++++++++++++++++++++++++++++++++++++++++++++ audio/sdl.c | 5 - config.def.h | 3 + driver.c | 3 + driver.h | 1 + settings.c | 3 + 8 files changed, 375 insertions(+), 5 deletions(-) create mode 100644 audio/dsound.c diff --git a/Makefile.win32 b/Makefile.win32 index 9524b66fea..7499eb66df 100644 --- a/Makefile.win32 +++ b/Makefile.win32 @@ -13,6 +13,7 @@ HAVE_SDL_IMAGE = 1 HAVE_XML = 1 HAVE_FREETYPE = 1 HAVE_XAUDIO = 1 +HAVE_DSOUND = 1 HAVE_RSOUND = 1 HAVE_DYLIB = 1 HAVE_NETPLAY = 1 @@ -59,6 +60,12 @@ ifeq ($(HAVE_XAUDIO), 1) DEFINES += -DHAVE_XAUDIO endif +ifeq ($(HAVE_DSOUND), 1) + OBJ += audio/dsound.o + DEFINES += -DHAVE_DSOUND + LIBS += -ldxguid -ldsound +endif + ifeq ($(HAVE_RSOUND), 1) OBJ += audio/rsound.o DEFINES += -DHAVE_RSOUND diff --git a/Makefile.win64 b/Makefile.win64 index cfaa2eafea..0aaa65c114 100644 --- a/Makefile.win64 +++ b/Makefile.win64 @@ -12,6 +12,7 @@ HAVE_SDL = 1 HAVE_XML = 1 HAVE_FREETYPE = 1 HAVE_XAUDIO = 1 +HAVE_DSOUND = 1 HAVE_RSOUND = 0 HAVE_DYLIB = 1 HAVE_NETPLAY = 1 @@ -52,6 +53,12 @@ ifeq ($(HAVE_XAUDIO), 1) DEFINES += -DHAVE_XAUDIO endif +ifeq ($(HAVE_DSOUND), 1) + OBJ += audio/dsound.o + DEFINES += -DHAVE_DSOUND + LIBS += -ldxguid -ldsound +endif + ifeq ($(HAVE_RSOUND), 1) OBJ += audio/rsound.o DEFINES += -DHAVE_RSOUND diff --git a/audio/dsound.c b/audio/dsound.c new file mode 100644 index 0000000000..0c2764c106 --- /dev/null +++ b/audio/dsound.c @@ -0,0 +1,351 @@ +/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes. + * Copyright (C) 2010-2011 - 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 . + */ + +#include "driver.h" +#include +#include +#include +#include +#include + +#include +#include "fifo_buffer.h" +#include "general.h" + +typedef struct dsound +{ + LPDIRECTSOUND ds; + LPDIRECTSOUNDBUFFER dsb; + HANDLE event; + bool nonblock; + + fifo_buffer_t *buffer; + CRITICAL_SECTION crit; + + volatile bool thread_alive; + HANDLE thread; + unsigned buffer_size; +} dsound_t; + +static inline unsigned write_avail(unsigned read_ptr, unsigned write_ptr, unsigned buffer_mask) +{ + return (read_ptr + buffer_mask + 1 - write_ptr) & buffer_mask; +} + +static inline void get_positions(dsound_t *ds, DWORD *read_ptr, DWORD *write_ptr) +{ + IDirectSoundBuffer_GetCurrentPosition(ds->dsb, read_ptr, write_ptr); +} + +#define CHUNK_SIZE 128 + +static inline void* grab_chunk(dsound_t *ds, DWORD write_ptr) +{ + void *chunk; + DWORD bytes; + HRESULT res = IDirectSoundBuffer_Lock(ds->dsb, write_ptr, CHUNK_SIZE, &chunk, &bytes, NULL, NULL, 0); + if (res == DSERR_BUFFERLOST) + { + res = IDirectSoundBuffer_Restore(ds->dsb); + if (res != DS_OK) + return NULL; + + res = IDirectSoundBuffer_Lock(ds->dsb, write_ptr, CHUNK_SIZE, &chunk, &bytes, NULL, NULL, 0); + if (res != DS_OK) + return NULL; + } + + if (bytes != CHUNK_SIZE) + { + SSNES_ERR("[DirectSound]: Could not lock as much data as requested, this should not happen!\n"); + IDirectSoundBuffer_Unlock(ds->dsb, chunk, bytes, NULL, 0); + return NULL; + } + + return chunk; +} + +static inline void release_chunk(dsound_t *ds, void *ptr) +{ + IDirectSoundBuffer_Unlock(ds->dsb, ptr, CHUNK_SIZE, NULL, 0); +} + + +static DWORD CALLBACK dsound_thread(PVOID data) +{ + dsound_t *ds = data; + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); + + unsigned buffer_mask = ds->buffer_size - 1; + DWORD write_ptr; + get_positions(ds, NULL, &write_ptr); + write_ptr = (write_ptr + ds->buffer_size / 2) & buffer_mask; + + while (ds->thread_alive) + { + DWORD read_ptr; + get_positions(ds, &read_ptr, NULL); + + DWORD avail = write_avail(read_ptr, write_ptr, buffer_mask); + if (avail > ds->buffer_size / 2) // We've underrun (or started to underrun) for some odd reason, skip ahead. + { + write_ptr = (read_ptr + ds->buffer_size - 2 * CHUNK_SIZE) & buffer_mask; + avail = 2 * CHUNK_SIZE; + } + + EnterCriticalSection(&ds->crit); + DWORD fifo_avail = fifo_read_avail(ds->buffer); + LeaveCriticalSection(&ds->crit); + + if (avail < CHUNK_SIZE) // No space to write. + { + Sleep(1); + // We could opt for using the notification interface, + // but it is not guaranteed to work, so use high priority sleeping patterns. :( + } + else if (fifo_avail < CHUNK_SIZE) // Got space to write, but nothing in FIFO (underrun), fill block with silence. + { + void *chunk; + if (!(chunk = grab_chunk(ds, write_ptr))) + { + ds->thread_alive = false; + break; + } + + memset(chunk, 0, CHUNK_SIZE); + release_chunk(ds, chunk); + write_ptr = (write_ptr + CHUNK_SIZE) & buffer_mask; + } + else // All is good. Pull from it and notify FIFO :D + { + void *chunk; + if (!(chunk = grab_chunk(ds, write_ptr))) + { + ds->thread_alive = false; + break; + } + + EnterCriticalSection(&ds->crit); + fifo_read(ds->buffer, chunk, CHUNK_SIZE); + LeaveCriticalSection(&ds->crit); + + release_chunk(ds, chunk); + write_ptr = (write_ptr + CHUNK_SIZE) & buffer_mask; + + SetEvent(ds->event); + } + } + + ExitThread(0); +} + +static void dsound_stop_thread(dsound_t *ds) +{ + if (ds->thread) + { + ds->thread_alive = false; + WaitForSingleObject(ds->thread, INFINITE); + CloseHandle(ds->thread); + ds->thread = NULL; + } +} + +static bool dsound_start_thread(dsound_t *ds) +{ + if (!ds->thread) + { + ds->thread_alive = true; + ds->thread = CreateThread(NULL, 0, dsound_thread, ds, 0, NULL); + if (ds->thread == NULL) + return false; + } + + return true; +} + +static void dsound_clear_buffer(dsound_t *ds) +{ + IDirectSoundBuffer_SetCurrentPosition(ds->dsb, 0); + void *ptr; + DWORD size; + + if (IDirectSoundBuffer_Lock(ds->dsb, 0, 0, &ptr, &size, NULL, NULL, DSBLOCK_ENTIREBUFFER) == DS_OK) + { + memset(ptr, 0, size); + IDirectSoundBuffer_Unlock(ds->dsb, ptr, size, NULL, 0); + } +} + +static void dsound_free(void *data) +{ + dsound_t *ds = data; + if (ds) + { + DeleteCriticalSection(&ds->crit); + + if (ds->thread) + { + ds->thread_alive = false; + WaitForSingleObject(ds->thread, INFINITE); + CloseHandle(ds->thread); + } + + if (ds->dsb) + { + IDirectSoundBuffer_Stop(ds->dsb); + IDirectSoundBuffer_Release(ds->dsb); + } + + if (ds) + IDirectSound_Release(ds->ds); + + if (ds->event) + CloseHandle(ds->event); + + if (ds->buffer) + fifo_free(ds->buffer); + + free(ds); + } +} + +static void* dsound_init(const char *device, unsigned rate, unsigned latency) +{ + dsound_t *ds = calloc(1, sizeof(*ds)); + if (!ds) + goto error; + + InitializeCriticalSection(&ds->crit); + + if (DirectSoundCreate(NULL, &ds->ds, NULL) != DS_OK) + goto error; + + if (IDirectSound_SetCooperativeLevel(ds->ds, GetDesktopWindow(), DSSCL_PRIORITY) != DS_OK) + goto error; + + WAVEFORMATEX wfx = { + .wFormatTag = WAVE_FORMAT_PCM, + .nChannels = 2, + .nSamplesPerSec = rate, + .wBitsPerSample = 16, + .nBlockAlign = 2 * sizeof(int16_t), + .nAvgBytesPerSec = rate * 2 * sizeof(int16_t), + }; + + ds->buffer_size = next_pow2((latency * wfx.nAvgBytesPerSec) / 1000); + SSNES_LOG("[DirectSound]: Setting buffer size of %u bytes\n", ds->buffer_size); + + DSBUFFERDESC bufdesc = { + .dwSize = sizeof(DSBUFFERDESC), + .dwFlags = DSBCAPS_GETCURRENTPOSITION2, + .dwBufferBytes = ds->buffer_size, + .lpwfxFormat = &wfx, + }; + + ds->event = CreateEvent(NULL, false, false, NULL); + if (!ds->event) + goto error; + + ds->buffer = fifo_new(4 * 1024); + if (!ds->buffer) + goto error; + + if (IDirectSound_CreateSoundBuffer(ds->ds, &bufdesc, &ds->dsb, 0) != DS_OK) + goto error; + + dsound_clear_buffer(ds); + + if (IDirectSoundBuffer_Play(ds->dsb, 0, 0, DSBPLAY_LOOPING) != DS_OK) + goto error; + + if (!dsound_start_thread(ds)) + goto error; + + return ds; + +error: + dsound_free(ds); + return NULL; +} + +static bool dsound_stop(void *data) +{ + dsound_t *ds = data; + dsound_stop_thread(ds); + return IDirectSoundBuffer_Stop(ds->dsb) == DS_OK; +} + +static bool dsound_start(void *data) +{ + dsound_t *ds = data; + dsound_clear_buffer(ds); + + if (!dsound_start_thread(ds)) + return false; + + return IDirectSoundBuffer_Play(ds->dsb, 0, 0, DSBPLAY_LOOPING) == DS_OK; +} + +static void dsound_set_nonblock_state(void *data, bool state) +{ + dsound_t *ds = data; + ds->nonblock = state; +} + +static ssize_t dsound_write(void *data, const void *buf_, size_t size) +{ + dsound_t *ds = data; + const uint8_t *buf = buf_; + + if (!ds->thread_alive) + return -1; + + size_t written = 0; + while (size > 0) + { + EnterCriticalSection(&ds->crit); + size_t avail = fifo_write_avail(ds->buffer); + if (avail > size) + avail = size; + + fifo_write(ds->buffer, buf, avail); + buf += avail; + size -= avail; + written += avail; + LeaveCriticalSection(&ds->crit); + + if (ds->nonblock || !ds->thread_alive) + break; + + if (avail == 0) + WaitForSingleObject(ds->event, INFINITE); + } + + return written; +} + + +const audio_driver_t audio_dsound = { + .init = dsound_init, + .write = dsound_write, + .stop = dsound_stop, + .start = dsound_start, + .set_nonblock_state = dsound_set_nonblock_state, + .free = dsound_free, + .ident = "dsound" +}; + diff --git a/audio/sdl.c b/audio/sdl.c index b1d9ffa5d1..f853c3b280 100644 --- a/audio/sdl.c +++ b/audio/sdl.c @@ -196,9 +196,4 @@ const audio_driver_t audio_sdl = { .free = sdl_audio_free, .ident = "sdl" }; - - - - - diff --git a/config.def.h b/config.def.h index 1e4502ab9e..f31172c698 100644 --- a/config.def.h +++ b/config.def.h @@ -57,6 +57,7 @@ #define AUDIO_XAUDIO 9 #define AUDIO_PULSE 10 #define AUDIO_EXT 15 +#define AUDIO_DSOUND 16 //////////////////////// #define INPUT_SDL 7 #define INPUT_X 12 @@ -86,6 +87,8 @@ #define AUDIO_DEFAULT_DRIVER AUDIO_AL #elif defined(HAVE_SDL) #define AUDIO_DEFAULT_DRIVER AUDIO_SDL +#elif defined(HAVE_DSOUND) +#define AUDIO_DEFAULT_DRIVER AUDIO_DSOUND #elif defined(HAVE_XAUDIO) #define AUDIO_DEFAULT_DRIVER AUDIO_XAUDIO #elif defined(HAVE_RSOUND) diff --git a/driver.c b/driver.c index 89cbaedc3b..0d9b5ccef1 100644 --- a/driver.c +++ b/driver.c @@ -53,6 +53,9 @@ static const audio_driver_t *audio_drivers[] = { #ifdef HAVE_XAUDIO &audio_xa, #endif +#ifdef HAVE_DSOUND + &audio_dsound, +#endif #ifdef HAVE_PULSE &audio_pulse, #endif diff --git a/driver.h b/driver.h index 09de73952b..30df26393c 100644 --- a/driver.h +++ b/driver.h @@ -158,6 +158,7 @@ extern const audio_driver_t audio_sdl; extern const audio_driver_t audio_xa; extern const audio_driver_t audio_pulse; extern const audio_driver_t audio_ext; +extern const audio_driver_t audio_dsound; extern const video_driver_t video_gl; extern const video_driver_t video_xvideo; extern const video_driver_t video_sdl; diff --git a/settings.c b/settings.c index d8ebeadb7c..e8dad3b9d9 100644 --- a/settings.c +++ b/settings.c @@ -77,6 +77,9 @@ static void set_defaults(void) case AUDIO_SDL: def_audio = "sdl"; break; + case AUDIO_DSOUND: + def_audio = "dsound"; + break; case AUDIO_XAUDIO: def_audio = "xaudio"; break; From 72c070f338cd403fc013cd1399daea4966c8a503 Mon Sep 17 00:00:00 2001 From: Themaister Date: Thu, 4 Aug 2011 18:52:00 +0200 Subject: [PATCH 11/26] Alter default ordering of audio drivers. --- config.def.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.def.h b/config.def.h index f31172c698..12477d45ae 100644 --- a/config.def.h +++ b/config.def.h @@ -85,10 +85,10 @@ #define AUDIO_DEFAULT_DRIVER AUDIO_JACK #elif defined(HAVE_AL) #define AUDIO_DEFAULT_DRIVER AUDIO_AL -#elif defined(HAVE_SDL) -#define AUDIO_DEFAULT_DRIVER AUDIO_SDL #elif defined(HAVE_DSOUND) #define AUDIO_DEFAULT_DRIVER AUDIO_DSOUND +#elif defined(HAVE_SDL) +#define AUDIO_DEFAULT_DRIVER AUDIO_SDL #elif defined(HAVE_XAUDIO) #define AUDIO_DEFAULT_DRIVER AUDIO_XAUDIO #elif defined(HAVE_RSOUND) From 4a05e50636fcaa63201ec73849a45c0ccfe9f6c0 Mon Sep 17 00:00:00 2001 From: Themaister Date: Thu, 4 Aug 2011 19:11:14 +0200 Subject: [PATCH 12/26] Minor progress. But no audio, hrm. --- audio/dsound.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/audio/dsound.c b/audio/dsound.c index 0c2764c106..7f60aa7ee2 100644 --- a/audio/dsound.c +++ b/audio/dsound.c @@ -101,11 +101,6 @@ static DWORD CALLBACK dsound_thread(PVOID data) get_positions(ds, &read_ptr, NULL); DWORD avail = write_avail(read_ptr, write_ptr, buffer_mask); - if (avail > ds->buffer_size / 2) // We've underrun (or started to underrun) for some odd reason, skip ahead. - { - write_ptr = (read_ptr + ds->buffer_size - 2 * CHUNK_SIZE) & buffer_mask; - avail = 2 * CHUNK_SIZE; - } EnterCriticalSection(&ds->crit); DWORD fifo_avail = fifo_read_avail(ds->buffer); From 4759e3411f53183cba13783369a39511cb1571c7 Mon Sep 17 00:00:00 2001 From: Themaister Date: Fri, 5 Aug 2011 02:05:32 +0200 Subject: [PATCH 13/26] Fix DSound up some more in Windows ... :D --- audio/dsound.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/audio/dsound.c b/audio/dsound.c index 7f60aa7ee2..313220d2c9 100644 --- a/audio/dsound.c +++ b/audio/dsound.c @@ -51,7 +51,7 @@ static inline void get_positions(dsound_t *ds, DWORD *read_ptr, DWORD *write_ptr IDirectSoundBuffer_GetCurrentPosition(ds->dsb, read_ptr, write_ptr); } -#define CHUNK_SIZE 128 +#define CHUNK_SIZE 256 static inline void* grab_chunk(dsound_t *ds, DWORD write_ptr) { @@ -84,7 +84,6 @@ static inline void release_chunk(dsound_t *ds, void *ptr) IDirectSoundBuffer_Unlock(ds->dsb, ptr, CHUNK_SIZE, NULL, 0); } - static DWORD CALLBACK dsound_thread(PVOID data) { dsound_t *ds = data; @@ -106,7 +105,8 @@ static DWORD CALLBACK dsound_thread(PVOID data) DWORD fifo_avail = fifo_read_avail(ds->buffer); LeaveCriticalSection(&ds->crit); - if (avail < CHUNK_SIZE) // No space to write. + // No space to write, or we don't have data in our fifo, but we can wait some time before it underruns ... + if (avail < CHUNK_SIZE || ((fifo_avail < CHUNK_SIZE) && (avail < ds->buffer_size / 2))) { Sleep(1); // We could opt for using the notification interface, @@ -118,6 +118,7 @@ static DWORD CALLBACK dsound_thread(PVOID data) if (!(chunk = grab_chunk(ds, write_ptr))) { ds->thread_alive = false; + SetEvent(ds->event); break; } @@ -131,6 +132,7 @@ static DWORD CALLBACK dsound_thread(PVOID data) if (!(chunk = grab_chunk(ds, write_ptr))) { ds->thread_alive = false; + SetEvent(ds->event); break; } @@ -243,10 +245,11 @@ static void* dsound_init(const char *device, unsigned rate, unsigned latency) ds->buffer_size = next_pow2((latency * wfx.nAvgBytesPerSec) / 1000); SSNES_LOG("[DirectSound]: Setting buffer size of %u bytes\n", ds->buffer_size); + SSNES_LOG("[DirectSound]: Latency = %u ms\n", (unsigned)((1000 * ds->buffer_size) / wfx.nAvgBytesPerSec)); DSBUFFERDESC bufdesc = { .dwSize = sizeof(DSBUFFERDESC), - .dwFlags = DSBCAPS_GETCURRENTPOSITION2, + .dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS, .dwBufferBytes = ds->buffer_size, .lpwfxFormat = &wfx, }; @@ -262,6 +265,9 @@ static void* dsound_init(const char *device, unsigned rate, unsigned latency) if (IDirectSound_CreateSoundBuffer(ds->ds, &bufdesc, &ds->dsb, 0) != DS_OK) goto error; + IDirectSoundBuffer_SetFrequency(ds->dsb, rate); + IDirectSoundBuffer_SetCurrentPosition(ds->dsb, 0); + dsound_clear_buffer(ds); if (IDirectSoundBuffer_Play(ds->dsb, 0, 0, DSBPLAY_LOOPING) != DS_OK) @@ -273,6 +279,7 @@ static void* dsound_init(const char *device, unsigned rate, unsigned latency) return ds; error: + SSNES_ERR("[DirectSound] Error occured in init!\n"); dsound_free(ds); return NULL; } From 014c3c90e0347142cf2420a801207d749dfb82fe Mon Sep 17 00:00:00 2001 From: Themaister Date: Fri, 5 Aug 2011 02:57:30 +0200 Subject: [PATCH 14/26] Fix possible race condition. --- audio/dsound.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/audio/dsound.c b/audio/dsound.c index 313220d2c9..7ea985606d 100644 --- a/audio/dsound.c +++ b/audio/dsound.c @@ -192,8 +192,6 @@ static void dsound_free(void *data) dsound_t *ds = data; if (ds) { - DeleteCriticalSection(&ds->crit); - if (ds->thread) { ds->thread_alive = false; @@ -201,6 +199,8 @@ static void dsound_free(void *data) CloseHandle(ds->thread); } + DeleteCriticalSection(&ds->crit); + if (ds->dsb) { IDirectSoundBuffer_Stop(ds->dsb); From 079a0100c0670b6de5cef0ff3807c65e59a5cf57 Mon Sep 17 00:00:00 2001 From: Themaister Date: Fri, 5 Aug 2011 03:06:48 +0200 Subject: [PATCH 15/26] Remove unneeded call. --- audio/dsound.c | 1 - 1 file changed, 1 deletion(-) diff --git a/audio/dsound.c b/audio/dsound.c index 7ea985606d..e379214bff 100644 --- a/audio/dsound.c +++ b/audio/dsound.c @@ -265,7 +265,6 @@ static void* dsound_init(const char *device, unsigned rate, unsigned latency) if (IDirectSound_CreateSoundBuffer(ds->ds, &bufdesc, &ds->dsb, 0) != DS_OK) goto error; - IDirectSoundBuffer_SetFrequency(ds->dsb, rate); IDirectSoundBuffer_SetCurrentPosition(ds->dsb, 0); dsound_clear_buffer(ds); From b337856f80afad57b5abe7f614d83561be2294d4 Mon Sep 17 00:00:00 2001 From: Themaister Date: Fri, 5 Aug 2011 13:35:02 +0200 Subject: [PATCH 16/26] More extensive error checking in dsound. --- audio/dsound.c | 50 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/audio/dsound.c b/audio/dsound.c index e379214bff..42df446b6d 100644 --- a/audio/dsound.c +++ b/audio/dsound.c @@ -52,27 +52,52 @@ static inline void get_positions(dsound_t *ds, DWORD *read_ptr, DWORD *write_ptr } #define CHUNK_SIZE 256 +#define BUFFER_ERROR ((void*)-1) static inline void* grab_chunk(dsound_t *ds, DWORD write_ptr) { - void *chunk; - DWORD bytes; + void *chunk = NULL; + DWORD bytes = 0; HRESULT res = IDirectSoundBuffer_Lock(ds->dsb, write_ptr, CHUNK_SIZE, &chunk, &bytes, NULL, NULL, 0); if (res == DSERR_BUFFERLOST) { res = IDirectSoundBuffer_Restore(ds->dsb); if (res != DS_OK) - return NULL; + return BUFFER_ERROR; res = IDirectSoundBuffer_Lock(ds->dsb, write_ptr, CHUNK_SIZE, &chunk, &bytes, NULL, NULL, 0); if (res != DS_OK) return NULL; } + const char *err; + switch (res) + { + case DSERR_BUFFERLOST: + err = "DSERR_BUFFERLOST"; + break; + case DSERR_INVALIDCALL: + err = "DSERR_INVALIDCALL"; + break; + case DSERR_INVALIDPARAM: + err = "DSERR_INVALIDPARAM"; + break; + case DSERR_PRIOLEVELNEEDED: + err = "DSERR_PRIOLEVELNEEDED"; + break; + + default: + err = NULL; + } + + if (err) + SSNES_ERR("[DirectSound Error]: %s\n", err); + if (bytes != CHUNK_SIZE) { SSNES_ERR("[DirectSound]: Could not lock as much data as requested, this should not happen!\n"); - IDirectSoundBuffer_Unlock(ds->dsb, chunk, bytes, NULL, 0); + if (chunk) + IDirectSoundBuffer_Unlock(ds->dsb, chunk, bytes, NULL, 0); return NULL; } @@ -115,12 +140,17 @@ static DWORD CALLBACK dsound_thread(PVOID data) else if (fifo_avail < CHUNK_SIZE) // Got space to write, but nothing in FIFO (underrun), fill block with silence. { void *chunk; - if (!(chunk = grab_chunk(ds, write_ptr))) + if ((chunk = grab_chunk(ds, write_ptr)) == BUFFER_ERROR) { ds->thread_alive = false; SetEvent(ds->event); break; } + else if (chunk == NULL) + { + Sleep(1); + continue; + } memset(chunk, 0, CHUNK_SIZE); release_chunk(ds, chunk); @@ -129,12 +159,17 @@ static DWORD CALLBACK dsound_thread(PVOID data) else // All is good. Pull from it and notify FIFO :D { void *chunk; - if (!(chunk = grab_chunk(ds, write_ptr))) + if ((chunk = grab_chunk(ds, write_ptr)) == BUFFER_ERROR) { ds->thread_alive = false; SetEvent(ds->event); break; } + else if (chunk == NULL) + { + Sleep(1); + continue; + } EnterCriticalSection(&ds->crit); fifo_read(ds->buffer, chunk, CHUNK_SIZE); @@ -324,10 +359,11 @@ static ssize_t dsound_write(void *data, const void *buf_, size_t size) avail = size; fifo_write(ds->buffer, buf, avail); + LeaveCriticalSection(&ds->crit); + buf += avail; size -= avail; written += avail; - LeaveCriticalSection(&ds->crit); if (ds->nonblock || !ds->thread_alive) break; From ac4219bcdec0b80c2b0e2c6772d8c6be6778e7f3 Mon Sep 17 00:00:00 2001 From: Themaister Date: Fri, 5 Aug 2011 13:39:27 +0200 Subject: [PATCH 17/26] Small clarification. --- audio/dsound.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audio/dsound.c b/audio/dsound.c index 42df446b6d..d6fcbb7ac7 100644 --- a/audio/dsound.c +++ b/audio/dsound.c @@ -91,7 +91,7 @@ static inline void* grab_chunk(dsound_t *ds, DWORD write_ptr) } if (err) - SSNES_ERR("[DirectSound Error]: %s\n", err); + SSNES_WARN("[DirectSound error]: %s\n", err); if (bytes != CHUNK_SIZE) { From 9aef33aabddc6cf9f531085724b025188b20a405 Mon Sep 17 00:00:00 2001 From: Themaister Date: Fri, 5 Aug 2011 14:01:58 +0200 Subject: [PATCH 18/26] More fixups in DSound. --- audio/dsound.c | 70 ++++++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 37 deletions(-) diff --git a/audio/dsound.c b/audio/dsound.c index d6fcbb7ac7..f6a6e9a1f9 100644 --- a/audio/dsound.c +++ b/audio/dsound.c @@ -54,20 +54,26 @@ static inline void get_positions(dsound_t *ds, DWORD *read_ptr, DWORD *write_ptr #define CHUNK_SIZE 256 #define BUFFER_ERROR ((void*)-1) -static inline void* grab_chunk(dsound_t *ds, DWORD write_ptr) +struct audio_lock { - void *chunk = NULL; - DWORD bytes = 0; - HRESULT res = IDirectSoundBuffer_Lock(ds->dsb, write_ptr, CHUNK_SIZE, &chunk, &bytes, NULL, NULL, 0); + void *chunk1; + DWORD size1; + void *chunk2; + DWORD size2; +}; + +static inline bool grab_region(dsound_t *ds, DWORD write_ptr, struct audio_lock *region) +{ + HRESULT res = IDirectSoundBuffer_Lock(ds->dsb, write_ptr, CHUNK_SIZE, ®ion->chunk1, ®ion->size1, ®ion->chunk2, ®ion->size2, 0); if (res == DSERR_BUFFERLOST) { res = IDirectSoundBuffer_Restore(ds->dsb); if (res != DS_OK) - return BUFFER_ERROR; + return false; - res = IDirectSoundBuffer_Lock(ds->dsb, write_ptr, CHUNK_SIZE, &chunk, &bytes, NULL, NULL, 0); + res = IDirectSoundBuffer_Lock(ds->dsb, write_ptr, CHUNK_SIZE, ®ion->chunk1, ®ion->size1, ®ion->chunk2, ®ion->size2, 0); if (res != DS_OK) - return NULL; + return false; } const char *err; @@ -91,22 +97,17 @@ static inline void* grab_chunk(dsound_t *ds, DWORD write_ptr) } if (err) - SSNES_WARN("[DirectSound error]: %s\n", err); - - if (bytes != CHUNK_SIZE) { - SSNES_ERR("[DirectSound]: Could not lock as much data as requested, this should not happen!\n"); - if (chunk) - IDirectSoundBuffer_Unlock(ds->dsb, chunk, bytes, NULL, 0); - return NULL; + SSNES_WARN("[DirectSound error]: %s\n", err); + return false; } - return chunk; + return true; } -static inline void release_chunk(dsound_t *ds, void *ptr) +static inline void release_region(dsound_t *ds, const struct audio_lock *region) { - IDirectSoundBuffer_Unlock(ds->dsb, ptr, CHUNK_SIZE, NULL, 0); + IDirectSoundBuffer_Unlock(ds->dsb, region->chunk1, region->size1, region->chunk2, region->size2); } static DWORD CALLBACK dsound_thread(PVOID data) @@ -139,44 +140,39 @@ static DWORD CALLBACK dsound_thread(PVOID data) } else if (fifo_avail < CHUNK_SIZE) // Got space to write, but nothing in FIFO (underrun), fill block with silence. { - void *chunk; - if ((chunk = grab_chunk(ds, write_ptr)) == BUFFER_ERROR) + struct audio_lock region; + if (!grab_region(ds, write_ptr, ®ion)) { ds->thread_alive = false; SetEvent(ds->event); break; } - else if (chunk == NULL) - { - Sleep(1); - continue; - } - memset(chunk, 0, CHUNK_SIZE); - release_chunk(ds, chunk); - write_ptr = (write_ptr + CHUNK_SIZE) & buffer_mask; + memset(region.chunk1, 0, region.size1); + memset(region.chunk2, 0, region.size2); + + release_region(ds, ®ion); + write_ptr = (write_ptr + region.size1 + region.size2) & buffer_mask; } else // All is good. Pull from it and notify FIFO :D { - void *chunk; - if ((chunk = grab_chunk(ds, write_ptr)) == BUFFER_ERROR) + struct audio_lock region; + if (!grab_region(ds, write_ptr, ®ion)) { ds->thread_alive = false; SetEvent(ds->event); break; } - else if (chunk == NULL) - { - Sleep(1); - continue; - } EnterCriticalSection(&ds->crit); - fifo_read(ds->buffer, chunk, CHUNK_SIZE); + if (region.chunk1) + fifo_read(ds->buffer, region.chunk1, region.size1); + if (region.chunk2) + fifo_read(ds->buffer, region.chunk2, region.size2); LeaveCriticalSection(&ds->crit); - release_chunk(ds, chunk); - write_ptr = (write_ptr + CHUNK_SIZE) & buffer_mask; + release_region(ds, ®ion); + write_ptr = (write_ptr + region.size1 + region.size2) & buffer_mask; SetEvent(ds->event); } From 706a3738b59e825643889755d20116f98e62f480 Mon Sep 17 00:00:00 2001 From: Themaister Date: Fri, 5 Aug 2011 19:17:33 +0200 Subject: [PATCH 19/26] Abandon POT buffer sizes only for dsound. Allows more fine tuned latencies. --- audio/dsound.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/audio/dsound.c b/audio/dsound.c index f6a6e9a1f9..f84b8b8775 100644 --- a/audio/dsound.c +++ b/audio/dsound.c @@ -41,9 +41,9 @@ typedef struct dsound unsigned buffer_size; } dsound_t; -static inline unsigned write_avail(unsigned read_ptr, unsigned write_ptr, unsigned buffer_mask) +static inline unsigned write_avail(unsigned read_ptr, unsigned write_ptr, unsigned buffer_size) { - return (read_ptr + buffer_mask + 1 - write_ptr) & buffer_mask; + return (read_ptr + buffer_size - write_ptr) % buffer_size; } static inline void get_positions(dsound_t *ds, DWORD *read_ptr, DWORD *write_ptr) @@ -115,17 +115,16 @@ static DWORD CALLBACK dsound_thread(PVOID data) dsound_t *ds = data; SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); - unsigned buffer_mask = ds->buffer_size - 1; DWORD write_ptr; get_positions(ds, NULL, &write_ptr); - write_ptr = (write_ptr + ds->buffer_size / 2) & buffer_mask; + write_ptr = (write_ptr + ds->buffer_size / 2) % ds->buffer_size; while (ds->thread_alive) { DWORD read_ptr; get_positions(ds, &read_ptr, NULL); - DWORD avail = write_avail(read_ptr, write_ptr, buffer_mask); + DWORD avail = write_avail(read_ptr, write_ptr, ds->buffer_size); EnterCriticalSection(&ds->crit); DWORD fifo_avail = fifo_read_avail(ds->buffer); @@ -152,7 +151,7 @@ static DWORD CALLBACK dsound_thread(PVOID data) memset(region.chunk2, 0, region.size2); release_region(ds, ®ion); - write_ptr = (write_ptr + region.size1 + region.size2) & buffer_mask; + write_ptr = (write_ptr + region.size1 + region.size2) % ds->buffer_size; } else // All is good. Pull from it and notify FIFO :D { @@ -172,7 +171,7 @@ static DWORD CALLBACK dsound_thread(PVOID data) LeaveCriticalSection(&ds->crit); release_region(ds, ®ion); - write_ptr = (write_ptr + region.size1 + region.size2) & buffer_mask; + write_ptr = (write_ptr + region.size1 + region.size2) % ds->buffer_size; SetEvent(ds->event); } @@ -274,7 +273,12 @@ static void* dsound_init(const char *device, unsigned rate, unsigned latency) .nAvgBytesPerSec = rate * 2 * sizeof(int16_t), }; - ds->buffer_size = next_pow2((latency * wfx.nAvgBytesPerSec) / 1000); + ds->buffer_size = (latency * wfx.nAvgBytesPerSec) / 1000; + ds->buffer_size /= CHUNK_SIZE; + ds->buffer_size *= CHUNK_SIZE; + if (ds->buffer_size < 4 * CHUNK_SIZE) + ds->buffer_size = 4 * CHUNK_SIZE; + SSNES_LOG("[DirectSound]: Setting buffer size of %u bytes\n", ds->buffer_size); SSNES_LOG("[DirectSound]: Latency = %u ms\n", (unsigned)((1000 * ds->buffer_size) / wfx.nAvgBytesPerSec)); From dadd794ece5027f5f1ff5e3cd79b95aeef15630a Mon Sep 17 00:00:00 2001 From: Themaister Date: Fri, 5 Aug 2011 19:18:43 +0200 Subject: [PATCH 20/26] Remove useless define. --- audio/dsound.c | 1 - 1 file changed, 1 deletion(-) diff --git a/audio/dsound.c b/audio/dsound.c index f84b8b8775..a962ffa292 100644 --- a/audio/dsound.c +++ b/audio/dsound.c @@ -52,7 +52,6 @@ static inline void get_positions(dsound_t *ds, DWORD *read_ptr, DWORD *write_ptr } #define CHUNK_SIZE 256 -#define BUFFER_ERROR ((void*)-1) struct audio_lock { From 6a260a57dddf49247a1cc1e96e62b8ac358f2e1b Mon Sep 17 00:00:00 2001 From: Themaister Date: Sat, 6 Aug 2011 03:28:07 +0200 Subject: [PATCH 21/26] Reset frame counter properly. --- gfx/gfx_common.c | 17 +++++++++++------ gfx/gfx_common.h | 1 + gfx/gl.c | 9 +++++---- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/gfx/gfx_common.c b/gfx/gfx_common.c index 62c2bfe1e8..ee49e8b755 100644 --- a/gfx/gfx_common.c +++ b/gfx/gfx_common.c @@ -25,21 +25,26 @@ static float tv_to_fps(const struct timeval *tv, const struct timeval *new_tv, i return frames/time; } +static unsigned gl_frames = 0; + +void gfx_window_title_reset(void) +{ + gl_frames = 0; +} + bool gfx_window_title(char *buf, size_t size) { - static int frames = 0; static struct timeval tv; struct timeval new_tv; bool ret = false; - if (frames == 0) + if (gl_frames == 0) { gettimeofday(&tv, NULL); snprintf(buf, size, "%s", g_extern.title_buf); ret = true; } - - if ((frames % 180) == 0 && frames > 0) + else if ((gl_frames % 180) == 0) { gettimeofday(&new_tv, NULL); struct timeval tmp_tv = tv; @@ -47,11 +52,11 @@ bool gfx_window_title(char *buf, size_t size) float fps = tv_to_fps(&tmp_tv, &new_tv, 180); - snprintf(buf, size, "%s || FPS: %6.1f || Frames: %d", g_extern.title_buf, fps, frames); + snprintf(buf, size, "%s || FPS: %6.1f || Frames: %d", g_extern.title_buf, fps, gl_frames); ret = true; } - frames++; + gl_frames++; return ret; } diff --git a/gfx/gfx_common.h b/gfx/gfx_common.h index 95486494c8..97baee203b 100644 --- a/gfx/gfx_common.h +++ b/gfx/gfx_common.h @@ -22,5 +22,6 @@ #include bool gfx_window_title(char *buf, size_t size); +void gfx_window_title_reset(void); #endif diff --git a/gfx/gl.c b/gfx/gl.c index 2866d4baad..ac6f469b0d 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -1051,6 +1051,11 @@ static void* gl_init(const video_info_t *video, const input_driver_t **input, vo if (!SDL_SetVideoMode(video->width, video->height, g_settings.video.force_16bit ? 16 : 0, SDL_OPENGL | SDL_RESIZABLE | (video->fullscreen ? SDL_FULLSCREEN : 0))) return NULL; + gfx_window_title_reset(); + char buf[128]; + if (gfx_window_title(buf, sizeof(buf))) + SDL_WM_SetCaption(buf, NULL); + // Remove that ugly mouse :D SDL_ShowCursor(SDL_DISABLE); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -1138,10 +1143,6 @@ static void* gl_init(const video_info_t *video, const input_driver_t **input, vo glColor4f(1, 1, 1, 1); glClearColor(0, 0, 0, 1); - char buf[128]; - if (gfx_window_title(buf, sizeof(buf))) - SDL_WM_SetCaption(buf, NULL); - glMatrixMode(GL_MODELVIEW); glLoadIdentity(); From 7425e1f879698119b0dd94d4d085d01ae873440c Mon Sep 17 00:00:00 2001 From: Themaister Date: Sun, 7 Aug 2011 15:00:34 +0200 Subject: [PATCH 22/26] Allow disabling composition in Win Vista/7Allow disabling composition in Win Vista/7. --- config.def.h | 3 +++ general.h | 1 + gfx/gfx_common.c | 35 +++++++++++++++++++++++++++++++++++ gfx/gfx_common.h | 4 ++++ gfx/gl.c | 4 ++++ settings.c | 2 ++ ssnes.cfg | 3 +++ 7 files changed, 52 insertions(+) diff --git a/config.def.h b/config.def.h index 12477d45ae..9f3bd6e56e 100644 --- a/config.def.h +++ b/config.def.h @@ -126,6 +126,9 @@ static const unsigned fullscreen_y = 0; // Force 16-bit colors. static const bool force_16bit = false; +// Forcibly disable composition. Only valid on Windows Vista/7 for now. +static const bool disable_composition = false; + // Video VSYNC (recommended) static const bool vsync = true; diff --git a/general.h b/general.h index 9494a8ed92..4754249e91 100644 --- a/general.h +++ b/general.h @@ -89,6 +89,7 @@ struct settings float msg_pos_y; bool force_16bit; + bool disable_composition; char external_driver[256]; } video; diff --git a/gfx/gfx_common.c b/gfx/gfx_common.c index ee49e8b755..5bf00bb6c7 100644 --- a/gfx/gfx_common.c +++ b/gfx/gfx_common.c @@ -60,4 +60,39 @@ bool gfx_window_title(char *buf, size_t size) return ret; } +#ifdef _WIN32 +#include +#include "dynamic.h" +void gfx_set_composition(void) +{ + if (!g_settings.video.disable_composition) + return; + + static bool inited = false; + if (inited) + return; + inited = true; + + dylib_t lib = dylib_load("dwmapi.dll"); + if (!lib) + { + SSNES_ERR("Did not find dwmapi.dll"); + return; + } + + HRESULT (WINAPI *composition_enable)(UINT) = (HRESULT (WINAPI*)(UINT))dylib_proc(lib, "DwmEnableComposition"); + if (!composition_enable) + { + SSNES_ERR("Did not find DwmEnableComposition ...\n"); + dylib_close(lib); + return; + } + + HRESULT ret = composition_enable(0); + if (FAILED(ret)) + SSNES_ERR("Failed to set composition state ...\n"); + + dylib_close(lib); +} +#endif diff --git a/gfx/gfx_common.h b/gfx/gfx_common.h index 97baee203b..2a24933da6 100644 --- a/gfx/gfx_common.h +++ b/gfx/gfx_common.h @@ -24,4 +24,8 @@ bool gfx_window_title(char *buf, size_t size); void gfx_window_title_reset(void); +#ifdef _WIN32 +void gfx_set_composition(void); +#endif + #endif diff --git a/gfx/gl.c b/gfx/gl.c index ac6f469b0d..3e844e6dca 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -1036,6 +1036,10 @@ static void gl_set_nonblock_state(void *data, bool state) static void* gl_init(const video_info_t *video, const input_driver_t **input, void **input_data) { +#ifdef _WIN32 + gfx_set_composition(); +#endif + if (SDL_Init(SDL_INIT_VIDEO) < 0) return NULL; diff --git a/settings.c b/settings.c index e8dad3b9d9..e0e9b1a32f 100644 --- a/settings.c +++ b/settings.c @@ -118,6 +118,7 @@ static void set_defaults(void) g_settings.video.fullscreen_x = fullscreen_x; g_settings.video.fullscreen_y = fullscreen_y; g_settings.video.force_16bit = force_16bit; + g_settings.video.disable_composition = disable_composition; g_settings.video.vsync = vsync; g_settings.video.smooth = video_smooth; g_settings.video.force_aspect = force_aspect; @@ -299,6 +300,7 @@ static void parse_config_file(void) } CONFIG_GET_BOOL(video.force_16bit, "video_force_16bit"); + CONFIG_GET_BOOL(video.disable_composition, "video_disable_composition"); CONFIG_GET_BOOL(video.vsync, "video_vsync"); CONFIG_GET_BOOL(video.smooth, "video_smooth"); CONFIG_GET_BOOL(video.force_aspect, "video_force_aspect"); diff --git a/ssnes.cfg b/ssnes.cfg index c31cb263cd..595f1d5368 100644 --- a/ssnes.cfg +++ b/ssnes.cfg @@ -25,6 +25,9 @@ # Force 16-bit colors. Apparently some video cards in use today have troubles with 32-bit ... # video_force_16bit = false +# Forcibly disable composition. Only works in Windows Vista/7 for now. +# video_disable_composition = false + # Video vsync. # video_vsync = true From fc2a39cd58b8ef9df45d2ae6a25141f9e51e0bff Mon Sep 17 00:00:00 2001 From: Themaister Date: Sun, 7 Aug 2011 21:15:50 +0200 Subject: [PATCH 23/26] More appropriate renaming. --- gfx/gfx_common.c | 18 +++++++++++++----- gfx/gfx_common.h | 2 +- gfx/gl.c | 2 +- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/gfx/gfx_common.c b/gfx/gfx_common.c index 5bf00bb6c7..abbe69a60d 100644 --- a/gfx/gfx_common.c +++ b/gfx/gfx_common.c @@ -63,11 +63,8 @@ bool gfx_window_title(char *buf, size_t size) #ifdef _WIN32 #include #include "dynamic.h" -void gfx_set_composition(void) +void gfx_set_dwm(void) { - if (!g_settings.video.disable_composition) - return; - static bool inited = false; if (inited) return; @@ -76,10 +73,20 @@ void gfx_set_composition(void) dylib_t lib = dylib_load("dwmapi.dll"); if (!lib) { - SSNES_ERR("Did not find dwmapi.dll"); + SSNES_LOG("Did not find dwmapi.dll"); return; } + HRESULT (WINAPI *mmcss)(BOOL) = (HRESULT (WINAPI*)(BOOL))dylib_proc(lib, "DwmEnableMMCSS"); + if (mmcss) + { + SSNES_LOG("Setting multimedia scheduling for DWM.\n"); + mmcss(TRUE); + } + + if (!g_settings.video.disable_composition) + return; + HRESULT (WINAPI *composition_enable)(UINT) = (HRESULT (WINAPI*)(UINT))dylib_proc(lib, "DwmEnableComposition"); if (!composition_enable) { @@ -94,5 +101,6 @@ void gfx_set_composition(void) dylib_close(lib); } + #endif diff --git a/gfx/gfx_common.h b/gfx/gfx_common.h index 2a24933da6..e73f757f02 100644 --- a/gfx/gfx_common.h +++ b/gfx/gfx_common.h @@ -25,7 +25,7 @@ bool gfx_window_title(char *buf, size_t size); void gfx_window_title_reset(void); #ifdef _WIN32 -void gfx_set_composition(void); +void gfx_set_dwm(void); #endif #endif diff --git a/gfx/gl.c b/gfx/gl.c index 3e844e6dca..4fd2a91d46 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -1037,7 +1037,7 @@ static void gl_set_nonblock_state(void *data, bool state) static void* gl_init(const video_info_t *video, const input_driver_t **input, void **input_data) { #ifdef _WIN32 - gfx_set_composition(); + gfx_set_dwm(); #endif if (SDL_Init(SDL_INIT_VIDEO) < 0) From 1fb53627ac4c5c8b1c8dbf569c96ea3cdb2cb7ff Mon Sep 17 00:00:00 2001 From: Themaister Date: Sun, 7 Aug 2011 21:18:51 +0200 Subject: [PATCH 24/26] Make sure to free lib. --- gfx/gfx_common.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gfx/gfx_common.c b/gfx/gfx_common.c index abbe69a60d..5d13cfa597 100644 --- a/gfx/gfx_common.c +++ b/gfx/gfx_common.c @@ -85,7 +85,7 @@ void gfx_set_dwm(void) } if (!g_settings.video.disable_composition) - return; + goto end; HRESULT (WINAPI *composition_enable)(UINT) = (HRESULT (WINAPI*)(UINT))dylib_proc(lib, "DwmEnableComposition"); if (!composition_enable) @@ -99,6 +99,7 @@ void gfx_set_dwm(void) if (FAILED(ret)) SSNES_ERR("Failed to set composition state ...\n"); +end: dylib_close(lib); } From a2ec78320b18fdb16cdbfa3f2c55b52dda308269 Mon Sep 17 00:00:00 2001 From: Themaister Date: Sun, 7 Aug 2011 22:31:59 +0200 Subject: [PATCH 25/26] Improve behavior a bit. --- gfx/gfx_common.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/gfx/gfx_common.c b/gfx/gfx_common.c index 5d13cfa597..89c47243aa 100644 --- a/gfx/gfx_common.c +++ b/gfx/gfx_common.c @@ -63,6 +63,17 @@ bool gfx_window_title(char *buf, size_t size) #ifdef _WIN32 #include #include "dynamic.h" +// We only load this library once, so we let it be unloaded at application shutdown, +// since unloading it early seems to cause issues on some systems. + +static dylib_t dwmlib = NULL; + +static void gfx_dwm_shutdown(void) +{ + if (dwmlib) + dylib_close(dwmlib); +} + void gfx_set_dwm(void) { static bool inited = false; @@ -70,14 +81,15 @@ void gfx_set_dwm(void) return; inited = true; - dylib_t lib = dylib_load("dwmapi.dll"); - if (!lib) + dwmlib = dylib_load("dwmapi.dll"); + if (!dwmlib) { SSNES_LOG("Did not find dwmapi.dll"); return; } + atexit(gfx_dwm_shutdown); - HRESULT (WINAPI *mmcss)(BOOL) = (HRESULT (WINAPI*)(BOOL))dylib_proc(lib, "DwmEnableMMCSS"); + HRESULT (WINAPI *mmcss)(BOOL) = (HRESULT (WINAPI*)(BOOL))dylib_proc(dwmlib, "DwmEnableMMCSS"); if (mmcss) { SSNES_LOG("Setting multimedia scheduling for DWM.\n"); @@ -85,22 +97,18 @@ void gfx_set_dwm(void) } if (!g_settings.video.disable_composition) - goto end; + return; - HRESULT (WINAPI *composition_enable)(UINT) = (HRESULT (WINAPI*)(UINT))dylib_proc(lib, "DwmEnableComposition"); + HRESULT (WINAPI *composition_enable)(UINT) = (HRESULT (WINAPI*)(UINT))dylib_proc(dwmlib, "DwmEnableComposition"); if (!composition_enable) { SSNES_ERR("Did not find DwmEnableComposition ...\n"); - dylib_close(lib); return; } HRESULT ret = composition_enable(0); if (FAILED(ret)) SSNES_ERR("Failed to set composition state ...\n"); - -end: - dylib_close(lib); } #endif From 777e6a53343afcf4dcee521c1863cfa91819470a Mon Sep 17 00:00:00 2001 From: Themaister Date: Sun, 7 Aug 2011 23:35:54 +0200 Subject: [PATCH 26/26] Better shader path semantics. --- gfx/ext.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/gfx/ext.c b/gfx/ext.c index d4e866e528..ce7fcbf42a 100644 --- a/gfx/ext.c +++ b/gfx/ext.c @@ -224,6 +224,15 @@ static bool setup_video(ext_t *ext, const video_info_t *video, const input_drive return false; } + const char *cg_shader = NULL; + const char *xml_shader = NULL; + enum ssnes_shader_type type = g_settings.video.shader_type; + if ((type == SSNES_SHADER_CG || type == SSNES_SHADER_AUTO) && *g_settings.video.cg_shader_path) + cg_shader = g_settings.video.cg_shader_path; + else if ((type == SSNES_SHADER_BSNES || type == SSNES_SHADER_AUTO) && *g_settings.video.bsnes_shader_path) + xml_shader = g_settings.video.bsnes_shader_path; + + ssnes_video_info_t info = { .width = video->width, .height = video->height, @@ -234,8 +243,8 @@ static bool setup_video(ext_t *ext, const video_info_t *video, const input_drive .smooth = video->smooth, .input_scale = video->input_scale, .color_format = video->rgb32 ? SSNES_COLOR_FORMAT_ARGB8888 : SSNES_COLOR_FORMAT_XRGB1555, - .xml_shader = g_settings.video.bsnes_shader_path, - .cg_shader = g_settings.video.cg_shader_path, + .xml_shader = xml_shader, + .cg_shader = cg_shader, .ttf_font = *g_settings.video.font_path ? g_settings.video.font_path : NULL, .ttf_font_size = g_settings.video.font_size };