diff --git a/Makefile b/Makefile index 7baff23a57..58212f2bec 100644 --- a/Makefile +++ b/Makefile @@ -77,9 +77,8 @@ ifneq ($(findstring Linux,$(OS)),) JOYCONFIG_OBJ += input/linuxraw_joypad.o endif -OBJ += autosave.o thread.o - ifeq ($(HAVE_THREADS), 1) + OBJ += autosave.o thread.o gfx/thread_wrapper.o ifeq ($(findstring Haiku,$(OS)),) LIBS += -lpthread endif diff --git a/Makefile.win b/Makefile.win index 79396f99bc..a038dece6a 100644 --- a/Makefile.win +++ b/Makefile.win @@ -99,7 +99,7 @@ ifeq ($(HAVE_SDL), 1) endif ifeq ($(HAVE_THREADS), 1) - OBJ += autosave.o thread.o + OBJ += autosave.o thread.o gfx/thread_wrapper.o DEFINES += -DHAVE_THREADS endif diff --git a/android/native/jni/Android.mk b/android/native/jni/Android.mk index 40bb394aa7..1039768f1c 100644 --- a/android/native/jni/Android.mk +++ b/android/native/jni/Android.mk @@ -6,7 +6,6 @@ HAVE_SINC := 1 HAVE_LOGGER := 1 include $(CLEAR_VARS) - ifeq ($(TARGET_ARCH),arm) LOCAL_CFLAGS += -DANDROID_ARM -marm LOCAL_ARM_MODE := arm @@ -52,7 +51,7 @@ ifeq ($(PERF_TEST), 1) LOCAL_CFLAGS += -DPERF_TEST endif -LOCAL_CFLAGS += -Wall -Wno-unused-function -O3 -fno-stack-protector -funroll-loops -DNDEBUG -DHAVE_GRIFFIN -DANDROID -DHAVE_DYNAMIC -DHAVE_OPENGL -DHAVE_FBO -DHAVE_OVERLAY -DHAVE_OPENGLES -DHAVE_VID_CONTEXT -DHAVE_OPENGLES2 -DGLSL_DEBUG -DHAVE_GLSL -DWANT_MINIZ -DHAVE_ZLIB -DINLINE=inline -DLSB_FIRST -DHAVE_THREADS -D__LIBRETRO__ -DRARCH_PERFORMANCE_MODE -DPACKAGE_VERSION=\"$(RARCH_VERSION)\" -std=gnu99 -I../../../deps/miniz +LOCAL_CFLAGS += -Wall -pthread -Wno-unused-function -O3 -fno-stack-protector -funroll-loops -DNDEBUG -DHAVE_GRIFFIN -DANDROID -DHAVE_DYNAMIC -DHAVE_OPENGL -DHAVE_FBO -DHAVE_OVERLAY -DHAVE_OPENGLES -DHAVE_VID_CONTEXT -DHAVE_OPENGLES2 -DGLSL_DEBUG -DHAVE_GLSL -DWANT_MINIZ -DHAVE_ZLIB -DINLINE=inline -DLSB_FIRST -DHAVE_THREADS -D__LIBRETRO__ -DRARCH_PERFORMANCE_MODE -DPACKAGE_VERSION=\"$(RARCH_VERSION)\" -std=gnu99 -I../../../deps/miniz LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -landroid -lEGL -lGLESv2 $(LOGGER_LDLIBS) -ldl diff --git a/android/phoenix/res/xml/prefs.xml b/android/phoenix/res/xml/prefs.xml index 30cfbf92a4..d9a77dcbb3 100644 --- a/android/phoenix/res/xml/prefs.xml +++ b/android/phoenix/res/xml/prefs.xml @@ -86,12 +86,16 @@ android:title="Aspect ratio" /> + - diff --git a/android/phoenix/src/org/retroarch/browser/RetroArch.java b/android/phoenix/src/org/retroarch/browser/RetroArch.java index 2e2c6d8e22..ad360f0da7 100644 --- a/android/phoenix/src/org/retroarch/browser/RetroArch.java +++ b/android/phoenix/src/org/retroarch/browser/RetroArch.java @@ -299,7 +299,7 @@ public class RetroArch extends Activity implements prefs.edit().putBoolean("first_time_refreshrate_calculate", true).commit(); AlertDialog.Builder alert = new AlertDialog.Builder(this) .setTitle("Calculate Refresh Rate") - .setMessage("It is highly recommended you run the refresh rate calibration test before you use RetroArch. Do you want to run it now?\n\nIf you choose No, you can run it at any time in the video preferences.") + .setMessage("It is highly recommended you run the refresh rate calibration test before you use RetroArch. Do you want to run it now?\n\nIf you choose No, you can run it at any time in the video preferences.\n\nIf you get performance problems even after calibration, please try threaded video driver in video preferences.") .setPositiveButton("Yes", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { @@ -371,6 +371,7 @@ public class RetroArch extends Activity implements config.setInt("input_autodetect_icade_profile_pad4", prefs.getInt("input_autodetect_icade_profile_pad4", 0)); config.setDouble("video_refresh_rate", getRefreshRate()); + config.setBoolean("video_threaded", prefs.getBoolean("video_threaded", false)); String aspect = prefs.getString("video_aspect_ratio", "auto"); if (aspect.equals("full")) { diff --git a/audio/resampler.h b/audio/resampler.h index 981c44f541..c5f803e8d7 100644 --- a/audio/resampler.h +++ b/audio/resampler.h @@ -31,12 +31,10 @@ #define M_PI 3.14159265358979323846264338327 #endif -typedef float sample_t; - struct resampler_data { - const sample_t *data_in; - sample_t *data_out; + const float *data_in; + float *data_out; size_t input_frames; size_t output_frames; diff --git a/audio/sinc.c b/audio/sinc.c index 8aeb6c9fb0..545f53c5f0 100644 --- a/audio/sinc.c +++ b/audio/sinc.c @@ -105,9 +105,9 @@ typedef struct rarch_sinc_resampler { - sample_t *phase_table; - sample_t *buffer_l; - sample_t *buffer_r; + float *phase_table; + float *buffer_l; + float *buffer_r; unsigned taps; @@ -116,7 +116,7 @@ typedef struct rarch_sinc_resampler // A buffer for phase_table, buffer_l and buffer_r are created in a single calloc(). // Ensure that we get as good cache locality as we can hope for. - sample_t *main_buffer; + float *main_buffer; } rarch_sinc_resampler_t; static inline double sinc(double val) @@ -405,8 +405,8 @@ static void resampler_sinc_process(void *re_, struct resampler_data *data) uint32_t ratio = PHASES / data->ratio; - const sample_t *input = data->data_in; - sample_t *output = data->data_out; + const float *input = data->data_in; + float *output = data->data_out; size_t frames = data->input_frames; size_t out_frames = 0; @@ -477,7 +477,7 @@ static void *resampler_sinc_new(double bandwidth_mod) #endif size_t elems = phase_elems + 4 * re->taps; - re->main_buffer = (sample_t*)aligned_alloc__(128, sizeof(sample_t) * elems); + re->main_buffer = (float*)aligned_alloc__(128, sizeof(float) * elems); if (!re->main_buffer) goto error; diff --git a/config.def.h b/config.def.h index 7a0cf21cac..779117199b 100644 --- a/config.def.h +++ b/config.def.h @@ -218,6 +218,9 @@ static const bool disable_composition = false; // Video VSYNC (recommended) static const bool vsync = true; +// Threaded video. Will possibly increase performance significantly at cost of worse synchronization and latency. +static const bool video_threaded = false; + // Smooths picture static const bool video_smooth = true; diff --git a/console/griffin/griffin.c b/console/griffin/griffin.c index 7471bd4956..b66d5101e5 100644 --- a/console/griffin/griffin.c +++ b/console/griffin/griffin.c @@ -393,11 +393,13 @@ THREAD #include "../../thread/xenon_sdl_threads.c" #elif defined(HAVE_THREADS) #include "../../thread.c" +#include "../../gfx/thread_wrapper.c" #ifndef RARCH_CONSOLE #include "../../autosave.c" #endif #endif + /*============================================================ NETPLAY ============================================================ */ diff --git a/driver.c b/driver.c index 364ecba733..083b7a86e8 100644 --- a/driver.c +++ b/driver.c @@ -23,6 +23,7 @@ #include "compat/posix_string.h" #include "audio/utils.h" #include "audio/resampler.h" +#include "gfx/thread_wrapper.h" #ifdef HAVE_X11 #include "gfx/context/x11_common.h" @@ -423,7 +424,7 @@ void init_audio(void) g_extern.audio_data.data_ptr = 0; rarch_assert(g_settings.audio.out_rate < g_settings.audio.in_rate * AUDIO_MAX_RATIO); - rarch_assert(g_extern.audio_data.outsamples = (sample_t*)malloc(outsamples_max * sizeof(sample_t))); + rarch_assert(g_extern.audio_data.outsamples = (float*)malloc(outsamples_max * sizeof(float))); if (g_extern.audio_active && g_settings.audio.rate_control) { @@ -492,6 +493,12 @@ static void compute_audio_buffer_statistics(void) static void compute_monitor_fps_statistics(void) { + if (g_settings.video.threaded) + { + RARCH_LOG("Monitor FPS estimation is disabled for threaded video.\n"); + return; + } + if (g_extern.measure_data.frame_time_samples_count < 2 * MEASURE_FRAME_TIME_SAMPLES_COUNT) { RARCH_LOG("Does not have enough samples for monitor refresh rate estimation. Requires to run for at least %u frames.\n", @@ -800,7 +807,22 @@ void init_video_input(void) video.rgb32 = g_extern.filter.active || (g_extern.system.pix_fmt == RETRO_PIXEL_FORMAT_XRGB8888); const input_driver_t *tmp = driver.input; - driver.video_data = video_init_func(&video, &driver.input, &driver.input_data); +#ifdef HAVE_THREADS + if (g_settings.video.threaded) + { + find_video_driver(); // Need to grab the "real" video driver interface on a reinit. + RARCH_LOG("Starting threaded video driver ...\n"); + if (!rarch_threaded_video_init(&driver.video, &driver.video_data, + &driver.input, &driver.input_data, + driver.video, &video)) + { + RARCH_ERR("Cannot open threaded video driver ... Exiting ...\n"); + rarch_fail(1, "init_video_input()"); + } + } + else +#endif + driver.video_data = video_init_func(&video, &driver.input, &driver.input_data); if (driver.video_data == NULL) { diff --git a/driver.h b/driver.h index 580999e379..59c06b63ee 100644 --- a/driver.h +++ b/driver.h @@ -280,6 +280,8 @@ typedef struct driver void *video_data; void *input_data; + bool threaded_video; + // Set if the respective handles are owned by RetroArch driver core. // Consoles upper logic will generally intialize the drivers before // the driver core initializes. It will then be up to upper logic diff --git a/driver_funcs.h b/driver_funcs.h index dd2461e31f..d6f776e679 100644 --- a/driver_funcs.h +++ b/driver_funcs.h @@ -18,7 +18,7 @@ #ifndef _RARCH_DRIVER_FUNCS_H #define _RARCH_DRIVER_FUNCS_H -#if !defined(HAVE_GRIFFIN) /* Normal */ +#if !defined(HAVE_GRIFFIN) || defined(ANDROID) /* Normal */ #define audio_init_func(device, rate, latency) driver.audio->init(device, rate, latency) #define audio_write_func(buf, size) driver.audio->write(driver.audio_data, buf, size) diff --git a/general.h b/general.h index 86c63d8d9b..044ff55e54 100644 --- a/general.h +++ b/general.h @@ -175,6 +175,7 @@ struct settings char filter_path[PATH_MAX]; enum rarch_shader_type shader_type; float refresh_rate; + bool threaded; bool render_to_texture; @@ -398,7 +399,7 @@ struct global bool use_float; bool mute; - sample_t *outsamples; + float *outsamples; int16_t *conv_outsamples; int16_t *rewind_buf; diff --git a/gfx/context/androidegl_ctx.c b/gfx/context/androidegl_ctx.c index ba775cff8c..ce0e76633d 100644 --- a/gfx/context/androidegl_ctx.c +++ b/gfx/context/androidegl_ctx.c @@ -15,10 +15,12 @@ */ #include "../../driver.h" +#include "../../general.h" #include "../gfx_common.h" #include "../gl_common.h" #include /* Requires NDK r5 or newer */ +#include #include "../../frontend/frontend_android.h" #include "../image.h" @@ -148,6 +150,10 @@ static bool gfx_ctx_init(void) goto error; } + ALooper *looper = ALooper_forThread(); + if (!looper) + ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS); + return true; error: diff --git a/gfx/d3d9/d3d9.cpp b/gfx/d3d9/d3d9.cpp index af21c38d44..c182325a23 100644 --- a/gfx/d3d9/d3d9.cpp +++ b/gfx/d3d9/d3d9.cpp @@ -1166,9 +1166,12 @@ static void *d3d9_init(const video_info_t *info, const input_driver_t **input, if (!vid) return nullptr; - void *dinput = input_dinput.init(); - *input = dinput ? &input_dinput : nullptr; - *input_data = dinput; + if (input && input_data) + { + void *dinput = input_dinput.init(); + *input = dinput ? &input_dinput : nullptr; + *input_data = dinput; + } return vid; } diff --git a/gfx/gl.c b/gfx/gl.c index fc8850f2c4..019c383d02 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -1717,7 +1717,8 @@ static void *gl_init(const video_info_t *video, const input_driver_t **input, vo gl_init_textures(gl, video); gl_init_textures_data(gl); - context_input_driver_func(input, input_data); + if (input && input_data) + context_input_driver_func(input, input_data); #ifndef HAVE_RMENU // Comes too early for console - moved to gl_start diff --git a/gfx/sdl_gfx.c b/gfx/sdl_gfx.c index 0f30ad2778..2b2319af86 100644 --- a/gfx/sdl_gfx.c +++ b/gfx/sdl_gfx.c @@ -231,16 +231,19 @@ static void *sdl_gfx_init(const video_info_t *video, const input_driver_t **inpu sdl_gfx_set_handles(); - sdl_input = input_sdl.init(); - if (sdl_input) + if (input && input_data) { - *input = &input_sdl; - *input_data = sdl_input; - } - else - { - *input = NULL; - *input_data = NULL; + sdl_input = input_sdl.init(); + if (sdl_input) + { + *input = &input_sdl; + *input_data = sdl_input; + } + else + { + *input = NULL; + *input_data = NULL; + } } sdl_init_font(vid, g_settings.video.font_path, g_settings.video.font_size); diff --git a/gfx/thread_wrapper.c b/gfx/thread_wrapper.c new file mode 100644 index 0000000000..39d8397458 --- /dev/null +++ b/gfx/thread_wrapper.c @@ -0,0 +1,587 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2013 - Hans-Kristian Arntzen + * + * RetroArch 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. + * + * RetroArch 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 RetroArch. + * If not, see . + */ + +#include "thread_wrapper.h" +#include "../thread.h" +#include "../general.h" +#include "../performance.h" +#include +#include +#include + +enum thread_cmd +{ + CMD_NONE = 0, + CMD_INIT, + CMD_SET_SHADER, + CMD_FREE, + CMD_ALIVE, // Blocking alive check. Used when paused. + CMD_SET_ROTATION, + CMD_READ_VIEWPORT, + CMD_SET_NONBLOCK, + +#ifdef HAVE_OVERLAY + CMD_OVERLAY_ENABLE, + CMD_OVERLAY_LOAD, + CMD_OVERLAY_TEX_GEOM, + CMD_OVERLAY_VERTEX_GEOM, + CMD_OVERLAY_FULL_SCREEN, + CMD_OVERLAY_SET_ALPHA, +#endif + + CMD_DUMMY = INT_MAX +}; + +typedef struct thread_video +{ + slock_t *lock; + scond_t *cond_cmd; + scond_t *cond_thread; + sthread_t *thread; + + video_info_t info; + const video_driver_t *driver; +#ifdef HAVE_OVERLAY + const video_overlay_interface_t *overlay; +#endif + void *driver_data; + const input_driver_t **input; + void **input_data; + + bool alive; + bool focus; + + enum thread_cmd send_cmd; + enum thread_cmd reply_cmd; + union + { + bool b; + int i; + float f; + const char *str; + void *v; + + struct + { + enum rarch_shader_type type; + const char *path; + unsigned index; + } set_shader; + + struct + { + float x, y, w, h; + } rect; + + struct + { + const uint32_t *data; + unsigned width; + unsigned height; + } image; + } cmd_data; + + struct rarch_viewport vp; + struct rarch_viewport read_vp; // Last viewport reported to caller. + + struct + { + slock_t *lock; + uint8_t *buffer; + unsigned width; + unsigned height; + unsigned pitch; + bool updated; + char msg[1024]; + } frame; + + video_driver_t video_thread; + +} thread_video_t; + +static void *thread_init_never_call(const video_info_t *video, const input_driver_t **input, void **input_data) +{ + (void)video; + (void)input; + (void)input_data; + RARCH_ERR("Sanity check fail! Threaded mustn't be reinit.\n"); + abort(); + return NULL; +} + +static void thread_reply(thread_video_t *thr, enum thread_cmd cmd) +{ + slock_lock(thr->lock); + thr->reply_cmd = cmd; + thr->send_cmd = CMD_NONE; + scond_signal(thr->cond_cmd); + slock_unlock(thr->lock); +} + +static void thread_loop(void *data) +{ + thread_video_t *thr = (thread_video_t*)data; + + for (;;) + { + bool updated = false; + slock_lock(thr->lock); + while (thr->send_cmd == CMD_NONE && !thr->frame.updated) + scond_wait(thr->cond_thread, thr->lock); + if (thr->frame.updated) + updated = true; + slock_unlock(thr->lock); + + switch (thr->send_cmd) + { + case CMD_INIT: + thr->driver_data = thr->driver->init(&thr->info, thr->input, thr->input_data); + thr->cmd_data.b = thr->driver_data; + thr->driver->viewport_info(thr->driver_data, &thr->vp); + thread_reply(thr, CMD_INIT); + break; + + case CMD_FREE: + if (thr->driver_data) + thr->driver->free(thr->driver_data); + thr->driver_data = NULL; + thread_reply(thr, CMD_FREE); + return; + + case CMD_SET_NONBLOCK: + thr->driver->set_nonblock_state(thr->driver_data, thr->cmd_data.b); + thread_reply(thr, CMD_SET_NONBLOCK); + break; + + case CMD_SET_ROTATION: + thr->driver->set_rotation(thr->driver_data, thr->cmd_data.i); + thread_reply(thr, CMD_SET_ROTATION); + break; + + case CMD_READ_VIEWPORT: + { + struct rarch_viewport vp = {0}; + thr->driver->viewport_info(thr->driver_data, &vp); + if (memcmp(&vp, &thr->read_vp, sizeof(vp)) == 0) // We can read safely + { + thr->cmd_data.b = thr->driver->read_viewport(thr->driver_data, (uint8_t*)thr->cmd_data.v); + thread_reply(thr, CMD_READ_VIEWPORT); + } + else // Viewport dimensions changed right after main thread read the async value. Cannot read safely. + { + thr->cmd_data.b = false; + thread_reply(thr, CMD_READ_VIEWPORT); + } + break; + } + + case CMD_SET_SHADER: + { + bool ret = thr->driver->set_shader(thr->driver_data, + thr->cmd_data.set_shader.type, + thr->cmd_data.set_shader.path, + thr->cmd_data.set_shader.index); + thr->cmd_data.b = ret; + thread_reply(thr, CMD_SET_SHADER); + break; + } + + case CMD_ALIVE: + thr->cmd_data.b = thr->driver->alive(thr->driver_data); + thread_reply(thr, CMD_ALIVE); + break; + +#ifdef HAVE_OVERLAY + case CMD_OVERLAY_ENABLE: + thr->overlay->enable(thr->driver_data, thr->cmd_data.b); + thread_reply(thr, CMD_OVERLAY_ENABLE); + break; + + case CMD_OVERLAY_LOAD: + thr->cmd_data.b = thr->overlay->load(thr->driver_data, + thr->cmd_data.image.data, + thr->cmd_data.image.width, + thr->cmd_data.image.height); + thread_reply(thr, CMD_OVERLAY_LOAD); + break; + + case CMD_OVERLAY_TEX_GEOM: + thr->overlay->tex_geom(thr->driver_data, + thr->cmd_data.rect.x, + thr->cmd_data.rect.y, + thr->cmd_data.rect.w, + thr->cmd_data.rect.h); + thread_reply(thr, CMD_OVERLAY_TEX_GEOM); + break; + + case CMD_OVERLAY_VERTEX_GEOM: + thr->overlay->vertex_geom(thr->driver_data, + thr->cmd_data.rect.x, + thr->cmd_data.rect.y, + thr->cmd_data.rect.w, + thr->cmd_data.rect.h); + thread_reply(thr, CMD_OVERLAY_VERTEX_GEOM); + break; + + case CMD_OVERLAY_FULL_SCREEN: + thr->overlay->full_screen(thr->driver_data, thr->cmd_data.b); + thread_reply(thr, CMD_OVERLAY_FULL_SCREEN); + break; + + case CMD_OVERLAY_SET_ALPHA: + thr->overlay->set_alpha(thr->driver_data, thr->cmd_data.f); + thread_reply(thr, CMD_OVERLAY_SET_ALPHA); + break; +#endif + + default: + thread_reply(thr, thr->send_cmd); + break; + } + + if (updated) + { + slock_lock(thr->frame.lock); + bool ret = thr->driver->frame(thr->driver_data, + thr->frame.buffer, thr->frame.width, thr->frame.height, + thr->frame.pitch, *thr->frame.msg ? thr->frame.msg : NULL); + slock_unlock(thr->frame.lock); + + bool alive = ret && thr->driver->alive(thr->driver_data); + bool focus = ret && thr->driver->focus(thr->driver_data); + + struct rarch_viewport vp = {0}; + thr->driver->viewport_info(thr->driver_data, &vp); + + slock_lock(thr->lock); + thr->alive = alive; + thr->focus = focus; + thr->frame.updated = false; + thr->vp = vp; + slock_unlock(thr->lock); + } + } +} + +static void thread_send_cmd(thread_video_t *thr, enum thread_cmd cmd) +{ + slock_lock(thr->lock); + thr->send_cmd = cmd; + thr->reply_cmd = CMD_NONE; + scond_signal(thr->cond_thread); + slock_unlock(thr->lock); +} + +static void thread_wait_reply(thread_video_t *thr, enum thread_cmd cmd) +{ + slock_lock(thr->lock); + while (cmd != thr->reply_cmd) + scond_wait(thr->cond_cmd, thr->lock); + slock_unlock(thr->lock); +} + +static bool thread_alive(void *data) +{ + thread_video_t *thr = (thread_video_t*)data; + if (g_extern.is_paused) + { + thread_send_cmd(thr, CMD_ALIVE); + thread_wait_reply(thr, CMD_ALIVE); + return thr->cmd_data.b; + } + else + { + slock_lock(thr->lock); + bool ret = thr->alive; + slock_unlock(thr->lock); + return ret; + } +} + +static bool thread_focus(void *data) +{ + thread_video_t *thr = (thread_video_t*)data; + slock_lock(thr->lock); + bool ret = thr->focus; + slock_unlock(thr->lock); + return ret; +} + +static bool thread_frame(void *data, const void *frame_, + unsigned width, unsigned height, unsigned pitch, const char *msg) +{ + if (!frame_) + return true; + + RARCH_PERFORMANCE_INIT(thread_frame); + RARCH_PERFORMANCE_START(thread_frame); + + thread_video_t *thr = (thread_video_t*)data; + unsigned copy_stride = width * (thr->info.rgb32 ? sizeof(uint32_t) : sizeof(uint16_t)); + + const uint8_t *src = (const uint8_t*)frame_; + uint8_t *dst = thr->frame.buffer; + + slock_lock(thr->lock); + // Drop frame if updated flag is still set, as thread is still working on last frame. + if (!thr->frame.updated) + { + slock_lock(thr->frame.lock); + for (unsigned h = 0; h < height; h++, src += pitch, dst += copy_stride) + memcpy(dst, src, copy_stride); + thr->frame.updated = true; + thr->frame.width = width; + thr->frame.height = height; + thr->frame.pitch = copy_stride; + + if (msg) + strlcpy(thr->frame.msg, msg, sizeof(thr->frame.msg)); + else + *thr->frame.msg = '\0'; + + scond_signal(thr->cond_thread); + slock_unlock(thr->frame.lock); + } + slock_unlock(thr->lock); + + RARCH_PERFORMANCE_STOP(thread_frame); + + return true; +} + +static void thread_set_nonblock_state(void *data, bool state) +{ + thread_video_t *thr = (thread_video_t*)data; + thr->cmd_data.b = state; + thread_send_cmd(thr, CMD_SET_NONBLOCK); + thread_wait_reply(thr, CMD_SET_NONBLOCK); +} + +static bool thread_init(thread_video_t *thr, const video_info_t *info, const input_driver_t **input, + void **input_data) +{ + thr->lock = slock_new(); + thr->frame.lock = slock_new(); + thr->cond_cmd = scond_new(); + thr->cond_thread = scond_new(); + thr->input = input; + thr->input_data = input_data; + thr->info = *info; + thr->alive = true; + thr->focus = true; + + size_t max_size = info->input_scale * RARCH_SCALE_BASE; + max_size *= max_size; + max_size *= info->rgb32 ? sizeof(uint32_t) : sizeof(uint16_t); + thr->frame.buffer = (uint8_t*)malloc(max_size); + if (!thr->frame.buffer) + return false; + + memset(thr->frame.buffer, 0x80, max_size); + + thr->thread = sthread_create(thread_loop, thr); + if (!thr->thread) + return false; + thread_send_cmd(thr, CMD_INIT); + thread_wait_reply(thr, CMD_INIT); + + return thr->cmd_data.b; +} + +static bool thread_set_shader(void *data, enum rarch_shader_type type, const char *path, unsigned index) +{ + thread_video_t *thr = (thread_video_t*)data; + thr->cmd_data.set_shader.type = type; + thr->cmd_data.set_shader.path = path; + thr->cmd_data.set_shader.index = index; + thread_send_cmd(thr, CMD_SET_SHADER); + thread_wait_reply(thr, CMD_SET_SHADER); + return thr->cmd_data.b; +} + +static void thread_set_rotation(void *data, unsigned rotation) +{ + thread_video_t *thr = (thread_video_t*)data; + thr->cmd_data.i = rotation; + thread_send_cmd(thr, CMD_SET_ROTATION); + thread_wait_reply(thr, CMD_SET_ROTATION); +} + +// This value is set async as stalling on the video driver for every query is too slow. +// This means this value might not be correct, so viewport reads are not supported for now. +static void thread_viewport_info(void *data, struct rarch_viewport *vp) +{ + thread_video_t *thr = (thread_video_t*)data; + slock_lock(thr->lock); + *vp = thr->vp; + + // Explicitly mem-copied so we can use memcmp correctly later. + memcpy(&thr->read_vp, &thr->vp, sizeof(thr->vp)); + slock_unlock(thr->lock); +} + +static bool thread_read_viewport(void *data, uint8_t *buffer) +{ + thread_video_t *thr = (thread_video_t*)data; + thr->cmd_data.v = buffer; + thread_send_cmd(thr, CMD_READ_VIEWPORT); + thread_wait_reply(thr, CMD_READ_VIEWPORT); + return thr->cmd_data.b; +} + +static void thread_free(void *data) +{ + thread_video_t *thr = (thread_video_t*)data; + if (!thr) + return; + + thread_send_cmd(thr, CMD_FREE); + thread_wait_reply(thr, CMD_FREE); + sthread_join(thr->thread); + + free(thr->frame.buffer); + slock_free(thr->frame.lock); + slock_free(thr->lock); + scond_free(thr->cond_cmd); + scond_free(thr->cond_thread); + + free(thr); +} + +#ifdef HAVE_OVERLAY +static void thread_overlay_enable(void *data, bool state) +{ + thread_video_t *thr = (thread_video_t*)data; + thr->cmd_data.b = state; + thread_send_cmd(thr, CMD_OVERLAY_ENABLE); + thread_wait_reply(thr, CMD_OVERLAY_ENABLE); +} + +static bool thread_overlay_load(void *data, const uint32_t *image, unsigned width, unsigned height) +{ + thread_video_t *thr = (thread_video_t*)data; + thr->cmd_data.image.data = image; + thr->cmd_data.image.width = width; + thr->cmd_data.image.height = height; + thread_send_cmd(thr, CMD_OVERLAY_LOAD); + thread_wait_reply(thr, CMD_OVERLAY_LOAD); + return thr->cmd_data.b; +} + +static void thread_overlay_tex_geom(void *data, float x, float y, float w, float h) +{ + thread_video_t *thr = (thread_video_t*)data; + thr->cmd_data.rect.x = x; + thr->cmd_data.rect.y = y; + thr->cmd_data.rect.w = w; + thr->cmd_data.rect.h = h; + thread_send_cmd(thr, CMD_OVERLAY_TEX_GEOM); + thread_wait_reply(thr, CMD_OVERLAY_TEX_GEOM); +} + +static void thread_overlay_vertex_geom(void *data, float x, float y, float w, float h) +{ + thread_video_t *thr = (thread_video_t*)data; + thr->cmd_data.rect.x = x; + thr->cmd_data.rect.y = y; + thr->cmd_data.rect.w = w; + thr->cmd_data.rect.h = h; + thread_send_cmd(thr, CMD_OVERLAY_VERTEX_GEOM); + thread_wait_reply(thr, CMD_OVERLAY_VERTEX_GEOM); +} + +static void thread_overlay_full_screen(void *data, bool enable) +{ + thread_video_t *thr = (thread_video_t*)data; + thr->cmd_data.b = enable; + thread_send_cmd(thr, CMD_OVERLAY_FULL_SCREEN); + thread_wait_reply(thr, CMD_OVERLAY_FULL_SCREEN); +} + +static void thread_overlay_set_alpha(void *data, float mod) +{ + thread_video_t *thr = (thread_video_t*)data; + thr->cmd_data.f = mod; + thread_send_cmd(thr, CMD_OVERLAY_SET_ALPHA); + thread_wait_reply(thr, CMD_OVERLAY_SET_ALPHA); +} + +static const video_overlay_interface_t thread_overlay = { + thread_overlay_enable, + thread_overlay_load, + thread_overlay_tex_geom, + thread_overlay_vertex_geom, + thread_overlay_full_screen, + thread_overlay_set_alpha, +}; + +static void thread_get_overlay_interface(void *data, const video_overlay_interface_t **iface) +{ + thread_video_t *thr = (thread_video_t*)data; + *iface = &thread_overlay; + thr->driver->overlay_interface(thr->driver_data, &thr->overlay); +} +#endif + +static const video_driver_t video_thread = { + thread_init_never_call, // Should never be called directly. + thread_frame, + thread_set_nonblock_state, + thread_alive, + thread_focus, + thread_set_shader, + thread_free, + "Thread wrapper", + thread_set_rotation, + thread_viewport_info, + thread_read_viewport, +#ifdef HAVE_OVERLAY + thread_get_overlay_interface, // get_overlay_interface +#endif +}; + +static void thread_set_callbacks(thread_video_t *thr, const video_driver_t *driver) +{ + thr->video_thread = video_thread; + // Disable optional features if not present. + if (!driver->read_viewport) + thr->video_thread.read_viewport = NULL; + if (!driver->set_rotation) + thr->video_thread.set_rotation = NULL; + if (!driver->set_shader) + thr->video_thread.set_shader = NULL; +#ifdef HAVE_OVERLAY + if (!driver->overlay_interface) + thr->video_thread.overlay_interface = NULL; +#endif +} + +bool rarch_threaded_video_init(const video_driver_t **out_driver, void **out_data, + const input_driver_t **input, void **input_data, + const video_driver_t *driver, const video_info_t *info) +{ + thread_video_t *thr = (thread_video_t*)calloc(1, sizeof(*thr)); + if (!thr) + return false; + + thread_set_callbacks(thr, driver); + + thr->driver = driver; + *out_driver = &thr->video_thread; + *out_data = thr; + return thread_init(thr, info, input, input_data); +} + + diff --git a/gfx/thread_wrapper.h b/gfx/thread_wrapper.h new file mode 100644 index 0000000000..6019803525 --- /dev/null +++ b/gfx/thread_wrapper.h @@ -0,0 +1,24 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2013 - Hans-Kristian Arntzen + * + * RetroArch 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. + * + * RetroArch 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 RetroArch. + * If not, see . + */ + +#include "../driver.h" +#include "../boolean.h" + +// Starts a video driver in a new thread. +// Access to video driver will be mediated through this driver. +bool rarch_threaded_video_init(const video_driver_t **out_driver, void **out_data, + const input_driver_t **input, void **input_data, + const video_driver_t *driver, const video_info_t *info); + diff --git a/gfx/xvideo.c b/gfx/xvideo.c index a4db814332..ded1a37cd7 100644 --- a/gfx/xvideo.c +++ b/gfx/xvideo.c @@ -464,14 +464,17 @@ static void *xv_init(const video_info_t *video, const input_driver_t **input, vo driver.video_display = (uintptr_t)xv->display; driver.video_window = (Window)xv->window; - xinput = input_x.init(); - if (xinput) + if (input && input_data) { - *input = &input_x; - *input_data = xinput; + xinput = input_x.init(); + if (xinput) + { + *input = &input_x; + *input_data = xinput; + } + else + *input = NULL; } - else - *input = NULL; init_yuv_tables(xv); xv_init_font(xv, g_settings.video.font_path, g_settings.video.font_size); diff --git a/msvc/msvc-2010/RetroArch-msvc2010.vcxproj b/msvc/msvc-2010/RetroArch-msvc2010.vcxproj index b4cc93f5a8..40e35b3f1a 100644 --- a/msvc/msvc-2010/RetroArch-msvc2010.vcxproj +++ b/msvc/msvc-2010/RetroArch-msvc2010.vcxproj @@ -88,7 +88,7 @@ Level3 Disabled - WIN32;HAVE_WIN32_D3D9;HAVE_CG;HAVE_GLSL;HAVE_ZLIB;WANT_MINIZ;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_SCREENSHOTS;HAVE_BSV_MOVIE;HAVE_DINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETPLAY;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_SINC;PACKAGE_VERSION="0.9.8";_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__ + WIN32;HAVE_WIN32_D3D9;HAVE_CG;HAVE_GLSL;HAVE_ZLIB;WANT_MINIZ;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_SCREENSHOTS;HAVE_BSV_MOVIE;HAVE_DINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETPLAY;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_SINC;PACKAGE_VERSION="0.9.8";_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\;$(CG_INC_PATH);%(AdditionalIncludeDirectories) MultiThreadedDebug CompileAsCpp @@ -108,7 +108,7 @@ Level3 Disabled - WIN32;HAVE_WIN32_D3D9;HAVE_CG;HAVE_GLSL;HAVE_ZLIB;WANT_MINIZ;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_SCREENSHOTS;HAVE_BSV_MOVIE;HAVE_DINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETPLAY;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_SINC;PACKAGE_VERSION="0.9.8";_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__ + WIN32;HAVE_WIN32_D3D9;HAVE_CG;HAVE_GLSL;HAVE_ZLIB;WANT_MINIZ;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_SCREENSHOTS;HAVE_BSV_MOVIE;HAVE_DINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETPLAY;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_SINC;PACKAGE_VERSION="0.9.8";_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\;$(CG_INC_PATH);%(AdditionalIncludeDirectories) MultiThreadedDebug CompileAsCpp @@ -130,7 +130,7 @@ MaxSpeed true true - WIN32;HAVE_WIN32_D3D9;HAVE_CG;HAVE_GLSL;HAVE_ZLIB;WANT_MINIZ;HAVE_SINC;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_SCREENSHOTS;HAVE_BSV_MOVIE;HAVE_DINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETPLAY;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;PACKAGE_VERSION="0.9.8";_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__ + WIN32;HAVE_WIN32_D3D9;HAVE_CG;HAVE_GLSL;HAVE_ZLIB;WANT_MINIZ;HAVE_SINC;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_SCREENSHOTS;HAVE_BSV_MOVIE;HAVE_DINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETPLAY;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;PACKAGE_VERSION="0.9.8";_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\;$(CG_INC_PATH);%(AdditionalIncludeDirectories) MultiThreaded CompileAsCpp @@ -154,7 +154,7 @@ MaxSpeed true true - WIN32;HAVE_WIN32_D3D9;HAVE_CG;HAVE_GLSL;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_SCREENSHOTS;HAVE_BSV_MOVIE;HAVE_DINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETPLAY;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_SINC;HAVE_ZLIB;WANT_MINIZ;PACKAGE_VERSION="0.9.8";_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__ + WIN32;HAVE_WIN32_D3D9;HAVE_CG;HAVE_GLSL;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_SCREENSHOTS;HAVE_BSV_MOVIE;HAVE_DINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETPLAY;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_SINC;HAVE_ZLIB;WANT_MINIZ;PACKAGE_VERSION="0.9.8";_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\;$(CG_INC_PATH);%(AdditionalIncludeDirectories) MultiThreaded CompileAsCpp @@ -196,6 +196,8 @@ + + diff --git a/msvc/msvc-2010/RetroArch-msvc2010.vcxproj.filters b/msvc/msvc-2010/RetroArch-msvc2010.vcxproj.filters index 1cb7219c01..98dac513ab 100644 --- a/msvc/msvc-2010/RetroArch-msvc2010.vcxproj.filters +++ b/msvc/msvc-2010/RetroArch-msvc2010.vcxproj.filters @@ -213,6 +213,12 @@ Source Files\deps + + Source Files\gfx + + + Source Files\input + diff --git a/msvc/msvc_compat.h b/msvc/msvc_compat.h index 43e1fe903b..eaa693e435 100644 --- a/msvc/msvc_compat.h +++ b/msvc/msvc_compat.h @@ -38,6 +38,7 @@ typedef int ssize_t; // Disable some of the annoying warnings. #pragma warning(disable : 4800) +#pragma warning(disable : 4805) #pragma warning(disable : 4244) #pragma warning(disable : 4305) #pragma warning(disable : 4146) diff --git a/record/ffemu.c b/record/ffemu.c index a7ffffe4ff..3b81a33410 100644 --- a/record/ffemu.c +++ b/record/ffemu.c @@ -1023,7 +1023,7 @@ static void ffemu_audio_resample(ffemu_t *handle, struct ffemu_audio_data *data) { // It's always two channels ... struct resampler_data info = {0}; - info.data_in = (const sample_t*)data->data; + info.data_in = (const float*)data->data; info.data_out = handle->audio.resample_out; info.input_frames = data->frames; info.ratio = handle->audio.ratio; diff --git a/retroarch.c b/retroarch.c index 06dd7de0cd..66cf2848de 100644 --- a/retroarch.c +++ b/retroarch.c @@ -366,7 +366,7 @@ static bool audio_flush(const int16_t *data, size_t samples) if (!g_extern.audio_active) return false; - const sample_t *output_data = NULL; + const float *output_data = NULL; unsigned output_frames = 0; struct resampler_data src_data = {0}; diff --git a/retroarch.cfg b/retroarch.cfg index 2237d20e35..22248ce2cf 100644 --- a/retroarch.cfg +++ b/retroarch.cfg @@ -65,6 +65,9 @@ # Video vsync. # video_vsync = true +# Use threaded video driver. Using this might improve performance at possible cost of latency and more video stuttering. +# video_threaded = false + # Smoothens picture with bilinear filtering. Should be disabled if using pixel shaders. # video_smooth = true diff --git a/settings.c b/settings.c index 8620e4fcaa..83093c3e13 100644 --- a/settings.c +++ b/settings.c @@ -159,6 +159,7 @@ void config_set_defaults(void) g_settings.video.fullscreen_y = fullscreen_y; g_settings.video.disable_composition = disable_composition; g_settings.video.vsync = vsync; + g_settings.video.threaded = video_threaded; g_settings.video.smooth = video_smooth; g_settings.video.force_aspect = force_aspect; g_settings.video.scale_integer = scale_integer; @@ -438,6 +439,7 @@ bool config_load_file(const char *path) CONFIG_GET_INT(video.monitor_index, "video_monitor_index"); CONFIG_GET_BOOL(video.disable_composition, "video_disable_composition"); CONFIG_GET_BOOL(video.vsync, "video_vsync"); + CONFIG_GET_BOOL(video.threaded, "video_threaded"); CONFIG_GET_BOOL(video.smooth, "video_smooth"); CONFIG_GET_BOOL(video.force_aspect, "video_force_aspect"); CONFIG_GET_BOOL(video.scale_integer, "video_scale_integer");