Merge pull request #139 from Themaister/thread_video

Threaded video driver
This commit is contained in:
Squarepusher 2013-02-19 14:17:00 -08:00
commit bd4d97337c
27 changed files with 719 additions and 47 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

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

View File

@ -86,12 +86,16 @@
android:title="Aspect ratio" />
</PreferenceCategory>
<PreferenceCategory android:title="Synchronization" >
<CheckBoxPreference
android:defaultValue="false"
android:key="video_threaded"
android:summary="Uses a multi-threaded video driver. Is likely to improve performance at the expense of slightly more latency and jitter. Use this if you have troubles getting good video and audio."
android:title="Threaded video driver" />
<EditTextPreference
android:key="video_refresh_rate"
android:numeric="decimal"
android:summary="Force a specific refresh rate to be detected. Only set manually if calibration reports wrong refresh rate."
android:title="Forced refresh rate (Hz)" />
<Preference
android:summary="Attempts to find the true refresh rate of monitor. Updates value in &apos;Force refresh rate (Hz)&apos; option. To help ensure accuracy, make sure no intense background services are running, and avoid triggering screensaver."
android:title="Calibrate refresh rate" >

View File

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

View File

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

View File

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

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

@ -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
============================================================ */

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

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

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

View File

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

View File

@ -15,10 +15,12 @@
*/
#include "../../driver.h"
#include "../../general.h"
#include "../gfx_common.h"
#include "../gl_common.h"
#include <EGL/egl.h> /* Requires NDK r5 or newer */
#include <android/looper.h>
#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:

View File

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

View File

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

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

587
gfx/thread_wrapper.c Normal file
View File

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

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

@ -88,7 +88,7 @@
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>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__</PreprocessorDefinitions>
<PreprocessorDefinitions>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</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\;$(CG_INC_PATH);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<CompileAs>CompileAsCpp</CompileAs>
@ -108,7 +108,7 @@
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>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__</PreprocessorDefinitions>
<PreprocessorDefinitions>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</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\;$(CG_INC_PATH);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<CompileAs>CompileAsCpp</CompileAs>
@ -130,7 +130,7 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>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__</PreprocessorDefinitions>
<PreprocessorDefinitions>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</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\;$(CG_INC_PATH);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<CompileAs>CompileAsCpp</CompileAs>
@ -154,7 +154,7 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>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__</PreprocessorDefinitions>
<PreprocessorDefinitions>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</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\;$(CG_INC_PATH);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<CompileAs>CompileAsCpp</CompileAs>
@ -196,6 +196,8 @@
<ClCompile Include="..\..\gfx\rpng\rpng.c" />
<ClCompile Include="..\..\gfx\shader_cg.c" />
<ClCompile Include="..\..\gfx\shader_glsl.c" />
<ClCompile Include="..\..\gfx\thread_wrapper.c" />
<ClCompile Include="..\..\input\overlay.c" />
<ClCompile Include="..\..\performance.c">
</ClCompile>
<ClCompile Include="..\..\command.c">

View File

@ -213,6 +213,12 @@
<ClCompile Include="..\..\deps\miniz\miniz.c">
<Filter>Source Files\deps</Filter>
</ClCompile>
<ClCompile Include="..\..\gfx\thread_wrapper.c">
<Filter>Source Files\gfx</Filter>
</ClCompile>
<ClCompile Include="..\..\input\overlay.c">
<Filter>Source Files\input</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="resource.h">

View File

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

View File

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

View File

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

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