From 843d07900221d4883e199f377de3d53d3f8d4a0f Mon Sep 17 00:00:00 2001 From: Themaister Date: Sat, 16 Feb 2013 02:21:43 +0100 Subject: [PATCH] Begin adding threaded video driver wrapper. --- Makefile | 3 +- Makefile.win | 2 +- config.def.h | 3 + driver.c | 24 +++- driver.h | 2 + general.h | 1 + gfx/d3d9/d3d9.cpp | 9 +- gfx/gl.c | 3 +- gfx/sdl_gfx.c | 21 +-- gfx/thread_wrapper.c | 316 +++++++++++++++++++++++++++++++++++++++++++ gfx/thread_wrapper.h | 24 ++++ gfx/xvideo.c | 15 +- retroarch.cfg | 3 + settings.c | 2 + 14 files changed, 405 insertions(+), 23 deletions(-) create mode 100644 gfx/thread_wrapper.c create mode 100644 gfx/thread_wrapper.h 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/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/driver.c b/driver.c index 364ecba733..5db2e09af8 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" @@ -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/general.h b/general.h index e6bfa575fe..4398447637 100644 --- a/general.h +++ b/general.h @@ -173,6 +173,7 @@ struct settings char filter_path[PATH_MAX]; enum rarch_shader_type shader_type; float refresh_rate; + bool threaded; bool render_to_texture; diff --git a/gfx/d3d9/d3d9.cpp b/gfx/d3d9/d3d9.cpp index 70efc7a69e..856838f6d3 100644 --- a/gfx/d3d9/d3d9.cpp +++ b/gfx/d3d9/d3d9.cpp @@ -1160,9 +1160,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 0ebd219329..ac3a6739b8 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -1691,7 +1691,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..6f5337b478 --- /dev/null +++ b/gfx/thread_wrapper.c @@ -0,0 +1,316 @@ +/* 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 +#include +#include + +enum thread_cmd +{ + CMD_NONE = 0, + CMD_INIT, + CMD_SET_SHADER, + CMD_FREE, + CMD_SET_ROTATION, + CMD_VIEWPORT_INFO, + CMD_READ_VIEWPORT, + CMD_SET_NONBLOCK, + + 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; + 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; + const char *str; + void *v; + } cmd_data; + + struct + { + slock_t *lock; + uint8_t *buffer; + unsigned width; + unsigned height; + unsigned pitch; + bool updated; + char msg[1024]; + } frame; + +} 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: + //fprintf(stderr, "CMD_INIT\n"); + thr->driver_data = thr->driver->init(&thr->info, thr->input, thr->input_data); + thr->cmd_data.b = thr->driver_data; + thread_reply(thr, CMD_INIT); + break; + + case CMD_FREE: + //fprintf(stderr, "CMD_FREE\n"); + if (thr->driver_data) + thr->driver->free(thr->driver_data); + thr->driver_data = NULL; + thread_reply(thr, CMD_FREE); + return; + + case CMD_SET_NONBLOCK: + //fprintf(stderr, "CMD_SET_NONBLOCK\n"); + thr->driver->set_nonblock_state(thr->driver_data, thr->cmd_data.b); + thread_reply(thr, CMD_SET_NONBLOCK); + break; + + default: + //fprintf(stderr, "CMD unknown ...\n"); + thread_reply(thr, thr->send_cmd); + break; + } + + if (updated) + { + //fprintf(stderr, "RUN FRAME\n"); + 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); + //fprintf(stderr, "Alive: %d, Focus: %d.\n", alive, focus); + + slock_lock(thr->lock); + thr->alive = alive; + thr->focus = focus; + thr->frame.updated = false; + 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; + 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; + + 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; + scond_signal(thr->cond_thread); + 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'; + + slock_unlock(thr->frame.lock); + } + slock_unlock(thr->lock); + + 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; + + thr->frame.buffer = (uint8_t*)malloc((info->rgb32 ? sizeof(uint32_t) : sizeof(uint16_t)) * + info->input_scale * info->input_scale * RARCH_SCALE_BASE * RARCH_SCALE_BASE); + if (!thr->frame.buffer) + return false; + + 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 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); +} + +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, + NULL, // set_shader + thread_free, + "Thread wrapper", + NULL, // set_rotation + NULL, // viewport_info + NULL, // read_viewport +#ifdef HAVE_OVERLAY + NULL, // get_overlay_interface +#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; + + thr->driver = driver; + *out_driver = &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/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 d6a798f20f..217924c87d 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");