Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 54 additions & 9 deletions gfx/video_driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -9127,45 +9127,84 @@ static int video_driver_vr_ctx(void)
return vr_ctx_id;
}

void video_driver_vr_content_clear(void)
/* The headset context's state belongs to the thread drawing its frames:
* under the threaded wrapper the video thread, so it runs there, after
* the frames already handed over. Those keep the poses, space and
* stereo state they were made with, and a sample predicts from the
* last one's display time. */
static uintptr_t video_driver_vr_ctx_run(uintptr_t (*func)(void*),
void *data)
{
#ifdef HAVE_THREADS
if (video_driver_thread_wrapper_active())
{
video_thread_wait_idle();
return video_thread_run_blocking(func, data);
}
#endif
return func(data);
}

#if defined(ANDROID)
static uintptr_t video_driver_vr_content_clear_cb(void *data)
{
(void)data;
gl_android_openxr_set_stereo(false);
return 0;
}
#endif

void video_driver_vr_content_clear(void)
{
#if defined(ANDROID)
video_driver_vr_ctx_run(video_driver_vr_content_clear_cb, NULL);
#endif
}

/* Called by the runloop once per iteration, BEFORE retro_run(), so the
* pose a core reads is the one the frame will be rendered with. */
bool video_driver_vr_sample_tracking(void)
static uintptr_t video_driver_vr_sample(void *data)
{
bool ok = false;
uint32_t flags = 0;
uint32_t *flags = (uint32_t*)data;
bool ok = false;

switch (video_driver_vr_ctx())
{
#if defined(ANDROID) && defined(HAVE_VULKAN)
case VR_CTX_VK:
ok = android_vk_openxr_sample_tracking();
flags = android_vk_openxr_take_frame_flags();
*flags = android_vk_openxr_take_frame_flags();
break;
#endif
#if defined(ANDROID)
case VR_CTX_GL:
ok = gl_android_openxr_sample_tracking();
flags = gl_android_openxr_take_frame_flags();
*flags = gl_android_openxr_take_frame_flags();
break;
#endif
default:
break;
}

return ok;
}

/* Called by the runloop once per iteration, BEFORE retro_run(), so the
* pose a core reads is the one the frame will be rendered with. */
bool video_driver_vr_sample_tracking(void)
{
bool ok = false;
uint32_t flags = 0;

if (video_driver_vr_ctx() != VR_CTX_NONE)
ok = video_driver_vr_ctx_run(video_driver_vr_sample, &flags) != 0;

video_vr_frame_flags = flags;

return ok;
}

static bool video_driver_set_vr_content_info(video_vr_content_info_t *info)
static uintptr_t video_driver_vr_content_info_apply(void *data)
{
video_vr_content_info_t *info = (video_vr_content_info_t*)data;
video_driver_state_t *video_st = video_state_get_ptr();
unsigned w = 0, h = 0;

Expand Down Expand Up @@ -9233,6 +9272,12 @@ static bool video_driver_set_vr_content_info(video_vr_content_info_t *info)
return true;
}

static bool video_driver_set_vr_content_info(video_vr_content_info_t *info)
{
return video_driver_vr_ctx_run(video_driver_vr_content_info_apply,
info) != 0;
}

bool video_driver_get_vr_head_pose(struct retro_vr_head_pose *out)
{
switch (video_driver_vr_ctx())
Expand Down
97 changes: 95 additions & 2 deletions gfx/video_thread_wrapper.c
Original file line number Diff line number Diff line change
Expand Up @@ -4294,6 +4294,21 @@ static void thread_hw_context_destroying(void *data)
}
}

/* Direct, as the runloop asks every iteration: the driver answers from
* what its headset thread publishes and from what only init and free
* change, which this thread waits out. */
static float thread_get_headset_refresh(void *data, float *rates,
unsigned cap, unsigned *count)
{
thread_video_t *thr = (thread_video_t*)data;

if ( thr && thr->driver_data
&& thr->poke && thr->poke->get_headset_refresh)
return thr->poke->get_headset_refresh(thr->driver_data, rates, cap,
count);
return 0.0f;
}

static void thread_set_texture_frame(void *data, const void *frame,
bool rgb32, unsigned dims, float alpha)
{
Expand Down Expand Up @@ -4664,7 +4679,8 @@ static const video_poke_interface_t thread_poke = {
NULL, /* texture_lend_ready */
NULL, /* get_last_present_wait: read on the video thread */
thread_set_view_count,
thread_hw_context_destroying
thread_hw_context_destroying,
thread_get_headset_refresh
};

/* Video thread, for video_thread_get_poke_interface(): installs the
Expand Down Expand Up @@ -4719,6 +4735,76 @@ static bool video_thread_wrapper_gfx_widgets_enabled(void *data)
}
#endif

#ifdef HAVE_OPENXR
typedef struct
{
thread_video_t *thr;
const video_vr_content_info_t *info;
struct retro_vr_frame_state *frame;
} video_thread_vr_call_t;

static uintptr_t video_thread_set_vr_content_info_cb(void *data)
{
video_thread_vr_call_t *call = (video_thread_vr_call_t*)data;
return call->thr->driver->set_vr_content_info(call->thr->driver_data,
call->info);
}

/* On the video thread: the stereo state it sets is the frame's. */
static bool video_thread_set_vr_content_info(void *data,
const video_vr_content_info_t *info)
{
video_thread_vr_call_t call;
thread_video_t *thr = (thread_video_t*)data;

if ( !thr || !thr->driver_data || !thr->driver
|| !thr->driver->set_vr_content_info)
return false;
call.thr = thr;
call.info = info;
call.frame = NULL;
return video_thread_run_blocking(video_thread_set_vr_content_info_cb,
&call) != 0;
}

static uintptr_t video_thread_get_vr_frame_state_cb(void *data)
{
video_thread_vr_call_t *call = (video_thread_vr_call_t*)data;
return call->thr->driver->get_vr_frame_state(call->thr->driver_data,
call->frame);
}

/* On the video thread: taking the poses marks them for the frame that
* submits them. */
static bool video_thread_get_vr_frame_state(void *data,
struct retro_vr_frame_state *out)
{
video_thread_vr_call_t call;
thread_video_t *thr = (thread_video_t*)data;

if ( !thr || !thr->driver_data || !thr->driver
|| !thr->driver->get_vr_frame_state)
return false;
call.thr = thr;
call.info = NULL;
call.frame = out;
return video_thread_run_blocking(video_thread_get_vr_frame_state_cb,
&call) != 0;
}

/* Direct, as cores ask every frame: the drivers answer from the session
* init made, which only free takes down. */
static unsigned video_thread_get_video_views_status(void *data)
{
thread_video_t *thr = (thread_video_t*)data;

if ( thr && thr->driver_data && thr->driver
&& thr->driver->get_video_views_status)
return thr->driver->get_video_views_status(thr->driver_data);
return 0;
}
#endif

static const video_driver_t video_thread = {
video_thread_init_never_call, /* Should never be called directly. */
video_thread_frame,
Expand Down Expand Up @@ -4755,7 +4841,14 @@ static const video_driver_t video_thread = {
/* Without this the runloop's invalidate stopped at the wrapper: the
* driver kept the core's image across a reset, and the ring kept
* handing it back across a close. */
video_thread_invalidate_hw_render_cache
video_thread_invalidate_hw_render_cache,
NULL, /* read_viewport_hdr */
NULL, /* font_backend: CMD_INIT/CMD_FREE read the driver's */
#ifdef HAVE_OPENXR
video_thread_get_vr_frame_state,
video_thread_set_vr_content_info,
video_thread_get_video_views_status,
#endif
};

static void video_thread_set_callbacks(thread_video_t *thr,
Expand Down
7 changes: 5 additions & 2 deletions retroarch.c
Original file line number Diff line number Diff line change
Expand Up @@ -1538,9 +1538,12 @@ static void driver_adjust_system_rates(

#ifdef HAVE_OPENXR
/* A headset whose rate fits the core paces it in the window's place:
* its rate and interval stand in for the display's. */
* its rate and interval stand in for the display's. Not under
* threaded video, where the driver's frame, which waits for the
* headset, does not hold the core. */
video_st->headset_vsync = settings->bools.video_vsync;
video_st->headset_interval = video_st->headset_vsync ? headset_fit : 0;
video_st->headset_interval = (video_st->headset_vsync
&& !video_driver_thread_wrapper_active()) ? headset_fit : 0;
if (video_st->headset_interval)
{
video_refresh_rate = video_st->headset_hz;
Expand Down
Loading
Loading