Begin adding threaded video driver wrapper.

This commit is contained in:
Themaister 2013-02-16 02:21:43 +01:00
parent ed55245551
commit 843d079002
14 changed files with 405 additions and 23 deletions

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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)
{

View File

@ -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

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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);

316
gfx/thread_wrapper.c Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "thread_wrapper.h"
#include "../thread.h"
#include "../general.h"
#include <stdlib.h>
#include <string.h>
#include <limits.h>
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);
}

24
gfx/thread_wrapper.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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);

View File

@ -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);

View File

@ -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

View File

@ -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");